1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: b2dpolypolygoncutter.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_basegfx.hxx"
33 #include <osl/diagnose.h>
34 #include <basegfx/numeric/ftools.hxx>
35 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
36 #include <basegfx/point/b2dpoint.hxx>
37 #include <basegfx/vector/b2dvector.hxx>
38 #include <basegfx/polygon/b2dpolygon.hxx>
39 #include <basegfx/polygon/b2dpolygontools.hxx>
40 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
41 #include <basegfx/range/b2drange.hxx>
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <basegfx/curve/b2dcubicbezier.hxx>
47 //////////////////////////////////////////////////////////////////////////////
53 //////////////////////////////////////////////////////////////////////////////
59 B2VectorOrientation meOrinetation
;
62 //////////////////////////////////////////////////////////////////////////////
73 //////////////////////////////////////////////////////////////////////////////
81 // to have the correct curve segments in the crossover checks,
82 // it is necessary to keep the original next vectors, too. Else,
83 // it may happen to use a already switched next vector which
84 // would interpolate the wrong comparison point
85 B2DVector maOriginalNext
;
88 //////////////////////////////////////////////////////////////////////////////
95 bool operator<(const SN
& rComp
) const
97 if(fTools::equal(mpPN
->maPoint
.getX(), rComp
.mpPN
->maPoint
.getX()))
99 if(fTools::equal(mpPN
->maPoint
.getY(), rComp
.mpPN
->maPoint
.getY()))
101 return (mpPN
->mnI
< rComp
.mpPN
->mnI
);
105 return fTools::less(mpPN
->maPoint
.getY(), rComp
.mpPN
->maPoint
.getY());
110 return fTools::less(mpPN
->maPoint
.getX(), rComp
.mpPN
->maPoint
.getX());
115 //////////////////////////////////////////////////////////////////////////////
117 typedef ::std::vector
< PN
> PNV
;
118 typedef ::std::vector
< VN
> VNV
;
119 typedef ::std::vector
< SN
> SNV
;
121 //////////////////////////////////////////////////////////////////////////////
126 const B2DPolyPolygon maOriginal
;
131 unsigned mbIsCurve
: 1;
132 unsigned mbChanged
: 1;
134 void impAddPolygon(const sal_uInt32 aPos
, const B2DPolygon
& rGeometry
)
136 const sal_uInt32
nCount(rGeometry
.count());
141 for(sal_uInt32
a(0); a
< nCount
; a
++)
143 const B2DPoint
aPoint(rGeometry
.getB2DPoint(a
));
144 aNewPN
.maPoint
= aPoint
;
145 aNewPN
.mnI
= aPos
+ a
;
146 aNewPN
.mnIP
= aPos
+ ((a
!= 0) ? a
- 1 : nCount
- 1);
147 aNewPN
.mnIN
= aPos
+ ((a
+ 1 == nCount
) ? 0 : a
+ 1);
148 maPNV
.push_back(aNewPN
);
152 aNewVN
.maPrev
= rGeometry
.getPrevControlPoint(a
) - aPoint
;
153 aNewVN
.maNext
= rGeometry
.getNextControlPoint(a
) - aPoint
;
154 aNewVN
.maOriginalNext
= aNewVN
.maNext
;
155 maVNV
.push_back(aNewVN
);
158 aNewSN
.mpPN
= &maPNV
[maPNV
.size() - 1];
159 maSNV
.push_back(aNewSN
);
163 bool impLeftOfEdges(const B2DVector
& rVecA
, const B2DVector
& rVecB
, const B2DVector
& rTest
)
165 // tests if rTest is left of both directed line segments along the line -rVecA, rVecB. Test is
167 if(rVecA
.cross(rVecB
) > 0.0)
169 // b is left turn seen from a, test if Test is left of both and so inside (left is seeen as inside)
170 const bool bBoolA(fTools::moreOrEqual(rVecA
.cross(rTest
), 0.0));
171 const bool bBoolB(fTools::lessOrEqual(rVecB
.cross(rTest
), 0.0));
173 return (bBoolA
&& bBoolB
);
177 // b is right turn seen from a, test if Test is right of both and so outside (left is seeen as inside)
178 const bool bBoolA(fTools::lessOrEqual(rVecA
.cross(rTest
), 0.0));
179 const bool bBoolB(fTools::moreOrEqual(rVecB
.cross(rTest
), 0.0));
181 return (!(bBoolA
&& bBoolB
));
185 void impSwitchNext(PN
& rPNa
, PN
& rPNb
)
187 ::std::swap(rPNa
.mnIN
, rPNb
.mnIN
);
191 VN
& rVNa
= maVNV
[rPNa
.mnI
];
192 VN
& rVNb
= maVNV
[rPNb
.mnI
];
194 ::std::swap(rVNa
.maNext
, rVNb
.maNext
);
203 B2DCubicBezier
createSegment(const PN
& rPN
, bool bPrev
) const
205 const B2DPoint
& rStart(rPN
.maPoint
);
206 const B2DPoint
& rEnd(maPNV
[bPrev
? rPN
.mnIP
: rPN
.mnIN
].maPoint
);
207 const B2DVector
& rCPA(bPrev
? maVNV
[rPN
.mnI
].maPrev
: maVNV
[rPN
.mnI
].maNext
);
208 // Use maOriginalNext, not maNext to create the original (yet unchanged)
209 // curve segment. Otherwise, this segment would NOT ne correct.
210 const B2DVector
& rCPB(bPrev
? maVNV
[maPNV
[rPN
.mnIP
].mnI
].maOriginalNext
: maVNV
[maPNV
[rPN
.mnIN
].mnI
].maPrev
);
212 return B2DCubicBezier(rStart
, rStart
+ rCPA
, rEnd
+ rCPB
, rEnd
);
215 void impHandleCommon(PN
& rPNa
, PN
& rPNb
)
219 const B2DCubicBezier
aNextA(createSegment(rPNa
, false));
220 const B2DCubicBezier
aPrevA(createSegment(rPNa
, true));
222 if(aNextA
.equal(aPrevA
))
224 // deadend on A (identical edge)
228 const B2DCubicBezier
aNextB(createSegment(rPNb
, false));
229 const B2DCubicBezier
aPrevB(createSegment(rPNb
, true));
231 if(aNextB
.equal(aPrevB
))
233 // deadend on B (identical edge)
237 if(aPrevA
.equal(aPrevB
))
239 // common edge in same direction
240 if(aNextA
.equal(aNextB
))
242 // common edge in same direction continues
247 // common edge in same direction leave
248 // action is done on enter
252 else if(aPrevA
.equal(aNextB
))
254 // common edge in opposite direction
255 if(aNextA
.equal(aPrevB
))
257 // common edge in opposite direction continues
262 // common edge in opposite direction leave
263 impSwitchNext(rPNa
, rPNb
);
266 else if(aNextA
.equal(aNextB
))
268 // common edge in same direction enter
270 PN
* pPNa2
= &maPNV
[rPNa
.mnIN
];
271 PN
* pPNb2
= &maPNV
[rPNb
.mnIN
];
276 const B2DCubicBezier
aNextA2(createSegment(*pPNa2
, false));
277 const B2DCubicBezier
aNextB2(createSegment(*pPNb2
, false));
279 if(aNextA2
.equal(aNextB2
))
281 pPNa2
= &maPNV
[pPNa2
->mnIN
];
282 pPNb2
= &maPNV
[pPNb2
->mnIN
];
289 while(bOnEdge
&& pPNa2
!= &rPNa
&& pPNa2
!= &rPNa
);
293 // loop over two identical polygon paths
298 // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
299 // at enter/leave. Check for crossover.
300 const B2DVector
aPrevCA(aPrevA
.interpolatePoint(0.5) - aPrevA
.getStartPoint());
301 const B2DVector
aNextCA(aNextA
.interpolatePoint(0.5) - aNextA
.getStartPoint());
302 const B2DVector
aPrevCB(aPrevB
.interpolatePoint(0.5) - aPrevB
.getStartPoint());
303 const bool bEnter(impLeftOfEdges(aPrevCA
, aNextCA
, aPrevCB
));
305 const B2DCubicBezier
aNextA2(createSegment(*pPNa2
, false));
306 const B2DCubicBezier
aPrevA2(createSegment(*pPNa2
, true));
307 const B2DCubicBezier
aNextB2(createSegment(*pPNb2
, false));
308 const B2DVector
aPrevCA2(aPrevA2
.interpolatePoint(0.5) - aPrevA2
.getStartPoint());
309 const B2DVector
aNextCA2(aNextA2
.interpolatePoint(0.5) - aNextA2
.getStartPoint());
310 const B2DVector
aNextCB2(aNextB2
.interpolatePoint(0.5) - aNextB2
.getStartPoint());
311 const bool bLeave(impLeftOfEdges(aPrevCA2
, aNextCA2
, aNextCB2
));
316 impSwitchNext(rPNa
, rPNb
);
320 else if(aNextA
.equal(aPrevB
))
322 // common edge in opposite direction enter
323 impSwitchNext(rPNa
, rPNb
);
327 // no common edges, check for crossover
328 const B2DVector
aPrevCA(aPrevA
.interpolatePoint(0.5) - aPrevA
.getStartPoint());
329 const B2DVector
aNextCA(aNextA
.interpolatePoint(0.5) - aNextA
.getStartPoint());
330 const B2DVector
aPrevCB(aPrevB
.interpolatePoint(0.5) - aPrevB
.getStartPoint());
331 const B2DVector
aNextCB(aNextB
.interpolatePoint(0.5) - aNextB
.getStartPoint());
333 const bool bEnter(impLeftOfEdges(aPrevCA
, aNextCA
, aPrevCB
));
334 const bool bLeave(impLeftOfEdges(aPrevCA
, aNextCA
, aNextCB
));
339 impSwitchNext(rPNa
, rPNb
);
345 const B2DPoint
& rNextA(maPNV
[rPNa
.mnIN
].maPoint
);
346 const B2DPoint
& rPrevA(maPNV
[rPNa
.mnIP
].maPoint
);
348 if(rNextA
.equal(rPrevA
))
354 const B2DPoint
& rNextB(maPNV
[rPNb
.mnIN
].maPoint
);
355 const B2DPoint
& rPrevB(maPNV
[rPNb
.mnIP
].maPoint
);
357 if(rNextB
.equal(rPrevB
))
363 if(rPrevA
.equal(rPrevB
))
365 // common edge in same direction
366 if(rNextA
.equal(rNextB
))
368 // common edge in same direction continues
373 // common edge in same direction leave
374 // action is done on enter
378 else if(rPrevA
.equal(rNextB
))
380 // common edge in opposite direction
381 if(rNextA
.equal(rPrevB
))
383 // common edge in opposite direction continues
388 // common edge in opposite direction leave
389 impSwitchNext(rPNa
, rPNb
);
392 else if(rNextA
.equal(rNextB
))
394 // common edge in same direction enter
396 PN
* pPNa2
= &maPNV
[rPNa
.mnIN
];
397 PN
* pPNb2
= &maPNV
[rPNb
.mnIN
];
402 const B2DPoint
& rNextA2(maPNV
[pPNa2
->mnIN
].maPoint
);
403 const B2DPoint
& rNextB2(maPNV
[pPNb2
->mnIN
].maPoint
);
405 if(rNextA2
.equal(rNextB2
))
407 pPNa2
= &maPNV
[pPNa2
->mnIN
];
408 pPNb2
= &maPNV
[pPNb2
->mnIN
];
415 while(bOnEdge
&& pPNa2
!= &rPNa
&& pPNa2
!= &rPNa
);
419 // loop over two identical polygon paths
424 // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
425 // at enter/leave. Check for crossover.
426 const B2DPoint
& aPointE(rPNa
.maPoint
);
427 const B2DVector
aPrevAE(rPrevA
- aPointE
);
428 const B2DVector
aNextAE(rNextA
- aPointE
);
429 const B2DVector
aPrevBE(rPrevB
- aPointE
);
431 const B2DPoint
& aPointL(pPNa2
->maPoint
);
432 const B2DVector
aPrevAL(maPNV
[pPNa2
->mnIP
].maPoint
- aPointL
);
433 const B2DVector
aNextAL(maPNV
[pPNa2
->mnIN
].maPoint
- aPointL
);
434 const B2DVector
aNextBL(maPNV
[pPNb2
->mnIN
].maPoint
- aPointL
);
436 const bool bEnter(impLeftOfEdges(aPrevAE
, aNextAE
, aPrevBE
));
437 const bool bLeave(impLeftOfEdges(aPrevAL
, aNextAL
, aNextBL
));
441 // crossover; switch start or end
442 impSwitchNext(rPNa
, rPNb
);
446 else if(rNextA
.equal(rPrevB
))
448 // common edge in opposite direction enter
449 impSwitchNext(rPNa
, rPNb
);
453 // no common edges, check for crossover
454 const B2DPoint
& aPoint(rPNa
.maPoint
);
455 const B2DVector
aPrevA(rPrevA
- aPoint
);
456 const B2DVector
aNextA(rNextA
- aPoint
);
457 const B2DVector
aPrevB(rPrevB
- aPoint
);
458 const B2DVector
aNextB(rNextB
- aPoint
);
460 const bool bEnter(impLeftOfEdges(aPrevA
, aNextA
, aPrevB
));
461 const bool bLeave(impLeftOfEdges(aPrevA
, aNextA
, aNextB
));
466 impSwitchNext(rPNa
, rPNb
);
474 // sort by point to identify common nodes
475 ::std::sort(maSNV
.begin(), maSNV
.end());
477 // handle common nodes
478 const sal_uInt32
nNodeCount(maSNV
.size());
480 for(sal_uInt32
a(0); a
< nNodeCount
- 1; a
++)
482 // test a before using it, not after. Also use nPointCount instead of aSortNodes.size()
483 PN
& rPNb
= *(maSNV
[a
].mpPN
);
485 for(sal_uInt32
b(a
+ 1); b
< nNodeCount
&& rPNb
.maPoint
.equal(maSNV
[b
].mpPN
->maPoint
); b
++)
487 impHandleCommon(rPNb
, *maSNV
[b
].mpPN
);
493 solver(const B2DPolygon
& rOriginal
)
494 : maOriginal(B2DPolyPolygon(rOriginal
)),
498 const sal_uInt32
nOriginalCount(rOriginal
.count());
502 B2DPolygon
aGeometry(tools::addPointsAtCutsAndTouches(rOriginal
));
503 aGeometry
.removeDoublePoints();
504 aGeometry
= tools::simplifyCurveSegments(aGeometry
);
505 mbIsCurve
= aGeometry
.areControlPointsUsed();
507 const sal_uInt32
nPointCount(aGeometry
.count());
509 // If it's not a pezier polygon, at least four points are needed to create
510 // a self-intersection. If it's a bezier polygon, the minimum point number
511 // is two, since with a single point You get a curve, but no self-intersection
512 if(nPointCount
> 3 || (nPointCount
> 1 && mbIsCurve
))
514 // reserve space in point, control and sort vector.
515 maSNV
.reserve(nPointCount
);
516 maPNV
.reserve(nPointCount
);
517 maVNV
.reserve(mbIsCurve
? nPointCount
: 0);
520 impAddPolygon(0, aGeometry
);
522 // solve common nodes
528 solver(const B2DPolyPolygon
& rOriginal
)
529 : maOriginal(rOriginal
),
533 sal_uInt32
nOriginalCount(maOriginal
.count());
537 B2DPolyPolygon
aGeometry(tools::addPointsAtCutsAndTouches(maOriginal
, true));
538 aGeometry
.removeDoublePoints();
539 aGeometry
= tools::simplifyCurveSegments(aGeometry
);
540 mbIsCurve
= aGeometry
.areControlPointsUsed();
541 nOriginalCount
= aGeometry
.count();
545 sal_uInt32
nPointCount(0);
549 for(a
= 0; a
< nOriginalCount
; a
++)
551 const B2DPolygon
aCandidate(aGeometry
.getB2DPolygon(a
));
552 const sal_uInt32
nCandCount(aCandidate
.count());
554 // If it's not a bezier curve, at least three points would be needed to have a
555 // topological relevant (not empty) polygon. Since its not known here if trivial
556 // edges (dead ends) will be kept or sorted out, add non-bezier polygons with
557 // more than one point.
558 // For bezier curves, the minimum for defining an area is also one.
561 nPointCount
+= nCandCount
;
567 // reserve space in point, control and sort vector.
568 maSNV
.reserve(nPointCount
);
569 maPNV
.reserve(nPointCount
);
570 maVNV
.reserve(mbIsCurve
? nPointCount
: 0);
573 sal_uInt32
nInsertIndex(0);
575 for(a
= 0; a
< nOriginalCount
; a
++)
577 const B2DPolygon
aCandidate(aGeometry
.getB2DPolygon(a
));
578 const sal_uInt32
nCandCount(aCandidate
.count());
580 // use same condition as above, the data vector is
584 impAddPolygon(nInsertIndex
, aCandidate
);
585 nInsertIndex
+= nCandCount
;
589 // solve common nodes
596 B2DPolyPolygon
getB2DPolyPolygon()
600 B2DPolyPolygon aRetval
;
601 const sal_uInt32
nCount(maPNV
.size());
602 sal_uInt32
nCountdown(nCount
);
604 for(sal_uInt32
a(0); nCountdown
&& a
< nCount
; a
++)
608 if(SAL_MAX_UINT32
!= rPN
.mnI
)
610 // unused node, start new part polygon
616 const B2DPoint
& rPoint
= pPNCurr
->maPoint
;
617 aNewPart
.append(rPoint
);
621 const VN
& rVNCurr
= maVNV
[pPNCurr
->mnI
];
623 if(!rVNCurr
.maPrev
.equalZero())
625 aNewPart
.setPrevControlPoint(aNewPart
.count() - 1, rPoint
+ rVNCurr
.maPrev
);
628 if(!rVNCurr
.maNext
.equalZero())
630 aNewPart
.setNextControlPoint(aNewPart
.count() - 1, rPoint
+ rVNCurr
.maNext
);
634 pPNCurr
->mnI
= SAL_MAX_UINT32
;
636 pPNCurr
= &(maPNV
[pPNCurr
->mnIN
]);
638 while(pPNCurr
!= &rPN
&& SAL_MAX_UINT32
!= pPNCurr
->mnI
);
641 aNewPart
.setClosed(true);
642 aRetval
.append(aNewPart
);
650 // no change, return original
656 //////////////////////////////////////////////////////////////////////////////
658 } // end of anonymous namespace
659 } // end of namespace basegfx
661 //////////////////////////////////////////////////////////////////////////////
667 //////////////////////////////////////////////////////////////////////////////
669 B2DPolyPolygon
solveCrossovers(const B2DPolyPolygon
& rCandidate
)
671 if(rCandidate
.count() > 1L)
673 solver
aSolver(rCandidate
);
674 return aSolver
.getB2DPolyPolygon();
682 //////////////////////////////////////////////////////////////////////////////
684 B2DPolyPolygon
solveCrossovers(const B2DPolygon
& rCandidate
)
686 solver
aSolver(rCandidate
);
687 return aSolver
.getB2DPolyPolygon();
690 //////////////////////////////////////////////////////////////////////////////
692 B2DPolyPolygon
stripNeutralPolygons(const B2DPolyPolygon
& rCandidate
)
694 B2DPolyPolygon aRetval
;
696 for(sal_uInt32
a(0L); a
< rCandidate
.count(); a
++)
698 const B2DPolygon
aCandidate(rCandidate
.getB2DPolygon(a
));
700 if(ORIENTATION_NEUTRAL
!= tools::getOrientation(aCandidate
))
702 aRetval
.append(aCandidate
);
709 //////////////////////////////////////////////////////////////////////////////
711 B2DPolyPolygon
stripDispensablePolygons(const B2DPolyPolygon
& rCandidate
, bool bKeepAboveZero
)
713 const sal_uInt32
nCount(rCandidate
.count());
714 B2DPolyPolygon aRetval
;
720 if(!bKeepAboveZero
&& ORIENTATION_POSITIVE
== tools::getOrientation(rCandidate
.getB2DPolygon(0L)))
722 aRetval
= rCandidate
;
728 ::std::vector
< StripHelper
> aHelpers
;
729 aHelpers
.resize(nCount
);
731 for(a
= 0L; a
< nCount
; a
++)
733 const B2DPolygon
aCandidate(rCandidate
.getB2DPolygon(a
));
734 StripHelper
* pNewHelper
= &(aHelpers
[a
]);
735 pNewHelper
->maRange
= tools::getRange(aCandidate
);
736 pNewHelper
->meOrinetation
= tools::getOrientation(aCandidate
);
737 pNewHelper
->mnDepth
= (ORIENTATION_NEGATIVE
== pNewHelper
->meOrinetation
? -1L : 0L);
740 for(a
= 0L; a
< nCount
- 1L; a
++)
742 const B2DPolygon
aCandA(rCandidate
.getB2DPolygon(a
));
743 StripHelper
& rHelperA
= aHelpers
[a
];
745 for(b
= a
+ 1L; b
< nCount
; b
++)
747 const B2DPolygon
aCandB(rCandidate
.getB2DPolygon(b
));
748 StripHelper
& rHelperB
= aHelpers
[b
];
749 const bool bAInB(rHelperB
.maRange
.isInside(rHelperA
.maRange
) && tools::isInside(aCandB
, aCandA
, true));
750 const bool bBInA(rHelperA
.maRange
.isInside(rHelperB
.maRange
) && tools::isInside(aCandA
, aCandB
, true));
755 if(rHelperA
.meOrinetation
== rHelperB
.meOrinetation
)
757 // two polys or two holes. Lower one of them to get one of them out of the way.
758 // Since each will be contained in the other one, both will be increased, too.
759 // So, for lowering, increase only one of them
764 // poly and hole. They neutralize, so get rid of both. Move securely below zero.
765 rHelperA
.mnDepth
= -((sal_Int32
)nCount
);
766 rHelperB
.mnDepth
= -((sal_Int32
)nCount
);
773 if(ORIENTATION_NEGATIVE
== rHelperB
.meOrinetation
)
784 if(ORIENTATION_NEGATIVE
== rHelperA
.meOrinetation
)
797 for(a
= 0L; a
< nCount
; a
++)
799 const StripHelper
& rHelper
= aHelpers
[a
];
800 bool bAcceptEntry(bKeepAboveZero
? 1L <= rHelper
.mnDepth
: 0L == rHelper
.mnDepth
);
804 aRetval
.append(rCandidate
.getB2DPolygon(a
));
813 //////////////////////////////////////////////////////////////////////////////
815 B2DPolyPolygon
prepareForPolygonOperation(const B2DPolygon
& rCandidate
)
817 solver
aSolver(rCandidate
);
818 B2DPolyPolygon
aRetval(stripNeutralPolygons(aSolver
.getB2DPolyPolygon()));
820 return correctOrientations(aRetval
);
823 B2DPolyPolygon
prepareForPolygonOperation(const B2DPolyPolygon
& rCandidate
)
825 solver
aSolver(rCandidate
);
826 B2DPolyPolygon
aRetval(stripNeutralPolygons(aSolver
.getB2DPolyPolygon()));
828 return correctOrientations(aRetval
);
831 B2DPolyPolygon
solvePolygonOperationOr(const B2DPolyPolygon
& rCandidateA
, const B2DPolyPolygon
& rCandidateB
)
833 if(!rCandidateA
.count())
837 else if(!rCandidateB
.count())
843 // concatenate polygons, solve crossovers and throw away all sub-polygons
844 // which have a depth other than 0.
845 B2DPolyPolygon
aRetval(rCandidateA
);
847 aRetval
.append(rCandidateB
);
848 aRetval
= solveCrossovers(aRetval
);
849 aRetval
= stripNeutralPolygons(aRetval
);
851 return stripDispensablePolygons(aRetval
, false);
855 B2DPolyPolygon
solvePolygonOperationXor(const B2DPolyPolygon
& rCandidateA
, const B2DPolyPolygon
& rCandidateB
)
857 if(!rCandidateA
.count())
861 else if(!rCandidateB
.count())
867 // XOR is pretty simple: By definition it is the simple concatenation of
868 // the single polygons since we imply XOR fill rule. Make it intersection-free
869 // and correct orientations
870 B2DPolyPolygon
aRetval(rCandidateA
);
872 aRetval
.append(rCandidateB
);
873 aRetval
= solveCrossovers(aRetval
);
874 aRetval
= stripNeutralPolygons(aRetval
);
876 return correctOrientations(aRetval
);
880 B2DPolyPolygon
solvePolygonOperationAnd(const B2DPolyPolygon
& rCandidateA
, const B2DPolyPolygon
& rCandidateB
)
882 if(!rCandidateA
.count())
884 return B2DPolyPolygon();
886 else if(!rCandidateB
.count())
888 return B2DPolyPolygon();
892 // concatenate polygons, solve crossovers and throw away all sub-polygons
893 // with a depth of < 1. This means to keep all polygons where at least two
894 // polygons do overlap.
895 B2DPolyPolygon
aRetval(rCandidateA
);
897 aRetval
.append(rCandidateB
);
898 aRetval
= solveCrossovers(aRetval
);
899 aRetval
= stripNeutralPolygons(aRetval
);
901 return stripDispensablePolygons(aRetval
, true);
905 B2DPolyPolygon
solvePolygonOperationDiff(const B2DPolyPolygon
& rCandidateA
, const B2DPolyPolygon
& rCandidateB
)
907 if(!rCandidateA
.count())
909 return B2DPolyPolygon();
911 else if(!rCandidateB
.count())
917 // Make B topologically to holes and append to A
918 B2DPolyPolygon
aRetval(rCandidateB
);
921 aRetval
.append(rCandidateA
);
923 // solve crossovers and throw away all sub-polygons which have a
924 // depth other than 0.
925 aRetval
= basegfx::tools::solveCrossovers(aRetval
);
926 aRetval
= basegfx::tools::stripNeutralPolygons(aRetval
);
928 return basegfx::tools::stripDispensablePolygons(aRetval
, false);
932 B2DPolyPolygon
mergeToSinglePolyPolygon(const std::vector
< basegfx::B2DPolyPolygon
>& rInput
)
934 std::vector
< basegfx::B2DPolyPolygon
> aInput(rInput
);
936 // first step: prepareForPolygonOperation and simple merge of non-overlapping
937 // PolyPolygons for speedup; this is possible for the wanted OR-operation
940 std::vector
< basegfx::B2DPolyPolygon
> aResult
;
941 aResult
.reserve(aInput
.size());
943 for(sal_uInt32
a(0); a
< aInput
.size(); a
++)
945 const basegfx::B2DPolyPolygon
aCandidate(prepareForPolygonOperation(aInput
[a
]));
949 const B2DRange
aCandidateRange(aCandidate
.getB2DRange());
950 bool bCouldMergeSimple(false);
952 for(sal_uInt32
b(0); !bCouldMergeSimple
&& b
< aResult
.size(); b
++)
954 basegfx::B2DPolyPolygon
aTarget(aResult
[b
]);
955 const B2DRange
aTargetRange(aTarget
.getB2DRange());
957 if(!aCandidateRange
.overlaps(aTargetRange
))
959 aTarget
.append(aCandidate
);
960 aResult
[b
] = aTarget
;
961 bCouldMergeSimple
= true;
965 if(!bCouldMergeSimple
)
967 aResult
.push_back(aCandidate
);
972 aResult
.push_back(aCandidate
);
979 // second step: melt pairwise to a single PolyPolygon
980 while(aInput
.size() > 1)
982 std::vector
< basegfx::B2DPolyPolygon
> aResult
;
983 aResult
.reserve((aInput
.size() / 2) + 1);
985 for(sal_uInt32
a(0); a
< aInput
.size(); a
+= 2)
987 if(a
+ 1 < aInput
.size())
989 // a pair for processing
990 aResult
.push_back(solvePolygonOperationOr(aInput
[a
], aInput
[a
+ 1]));
994 // last single PolyPolygon; copy to target to not lose it
995 aResult
.push_back(aInput
[a
]);
1002 // third step: get result
1003 if(1 == aInput
.size())
1008 return B2DPolyPolygon();
1011 //////////////////////////////////////////////////////////////////////////////
1013 } // end of namespace tools
1014 } // end of namespace basegfx
1016 //////////////////////////////////////////////////////////////////////////////