Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
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>
33 :
34 : //////////////////////////////////////////////////////////////////////////////
35 :
36 : namespace basegfx
37 : {
38 : namespace
39 : {
40 : //////////////////////////////////////////////////////////////////////////////
41 :
42 374 : struct StripHelper
43 : {
44 : B2DRange maRange;
45 : sal_Int32 mnDepth;
46 : B2VectorOrientation meOrinetation;
47 : };
48 :
49 : //////////////////////////////////////////////////////////////////////////////
50 :
51 7666 : struct PN
52 : {
53 : public:
54 : B2DPoint maPoint;
55 : sal_uInt32 mnI;
56 : sal_uInt32 mnIP;
57 : sal_uInt32 mnIN;
58 : };
59 :
60 : //////////////////////////////////////////////////////////////////////////////
61 :
62 836 : struct VN
63 : {
64 : public:
65 : B2DVector maPrev;
66 : B2DVector maNext;
67 :
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;
73 : };
74 :
75 : //////////////////////////////////////////////////////////////////////////////
76 :
77 : struct SN
78 : {
79 : public:
80 : PN* mpPN;
81 :
82 17299 : bool operator<(const SN& rComp) const
83 : {
84 17299 : if(fTools::equal(mpPN->maPoint.getX(), rComp.mpPN->maPoint.getX()))
85 : {
86 4055 : if(fTools::equal(mpPN->maPoint.getY(), rComp.mpPN->maPoint.getY()))
87 : {
88 932 : return (mpPN->mnI < rComp.mpPN->mnI);
89 : }
90 : else
91 : {
92 3123 : return fTools::less(mpPN->maPoint.getY(), rComp.mpPN->maPoint.getY());
93 : }
94 : }
95 : else
96 : {
97 13244 : return fTools::less(mpPN->maPoint.getX(), rComp.mpPN->maPoint.getX());
98 : }
99 : }
100 : };
101 :
102 : //////////////////////////////////////////////////////////////////////////////
103 :
104 : typedef ::std::vector< PN > PNV;
105 : typedef ::std::vector< VN > VNV;
106 : typedef ::std::vector< SN > SNV;
107 :
108 : //////////////////////////////////////////////////////////////////////////////
109 :
110 205 : class solver
111 : {
112 : private:
113 : const B2DPolyPolygon maOriginal;
114 : PNV maPNV;
115 : VNV maVNV;
116 : SNV maSNV;
117 :
118 : unsigned mbIsCurve : 1;
119 : unsigned mbChanged : 1;
120 :
121 404 : void impAddPolygon(const sal_uInt32 aPos, const B2DPolygon& rGeometry)
122 : {
123 404 : const sal_uInt32 nCount(rGeometry.count());
124 404 : PN aNewPN;
125 404 : VN aNewVN;
126 : SN aNewSN;
127 :
128 3833 : for(sal_uInt32 a(0); a < nCount; a++)
129 : {
130 3429 : const B2DPoint aPoint(rGeometry.getB2DPoint(a));
131 3429 : aNewPN.maPoint = aPoint;
132 3429 : aNewPN.mnI = aPos + a;
133 3429 : aNewPN.mnIP = aPos + ((a != 0) ? a - 1 : nCount - 1);
134 3429 : aNewPN.mnIN = aPos + ((a + 1 == nCount) ? 0 : a + 1);
135 3429 : maPNV.push_back(aNewPN);
136 :
137 3429 : if(mbIsCurve)
138 : {
139 14 : aNewVN.maPrev = rGeometry.getPrevControlPoint(a) - aPoint;
140 14 : aNewVN.maNext = rGeometry.getNextControlPoint(a) - aPoint;
141 14 : aNewVN.maOriginalNext = aNewVN.maNext;
142 14 : maVNV.push_back(aNewVN);
143 : }
144 :
145 3429 : aNewSN.mpPN = &maPNV[maPNV.size() - 1];
146 3429 : maSNV.push_back(aNewSN);
147 3833 : }
148 404 : }
149 :
150 1396 : bool impLeftOfEdges(const B2DVector& rVecA, const B2DVector& rVecB, const B2DVector& rTest)
151 : {
152 : // tests if rTest is left of both directed line segments along the line -rVecA, rVecB. Test is
153 : // with border.
154 1396 : if(rVecA.cross(rVecB) > 0.0)
155 : {
156 : // b is left turn seen from a, test if Test is left of both and so inside (left is seeen as inside)
157 148 : const bool bBoolA(fTools::moreOrEqual(rVecA.cross(rTest), 0.0));
158 148 : const bool bBoolB(fTools::lessOrEqual(rVecB.cross(rTest), 0.0));
159 :
160 148 : return (bBoolA && bBoolB);
161 : }
162 : else
163 : {
164 : // b is right turn seen from a, test if Test is right of both and so outside (left is seeen as inside)
165 1248 : const bool bBoolA(fTools::lessOrEqual(rVecA.cross(rTest), 0.0));
166 1248 : const bool bBoolB(fTools::moreOrEqual(rVecB.cross(rTest), 0.0));
167 :
168 1248 : return (!(bBoolA && bBoolB));
169 : }
170 : }
171 :
172 699 : void impSwitchNext(PN& rPNa, PN& rPNb)
173 : {
174 699 : ::std::swap(rPNa.mnIN, rPNb.mnIN);
175 :
176 699 : if(mbIsCurve)
177 : {
178 1 : VN& rVNa = maVNV[rPNa.mnI];
179 1 : VN& rVNb = maVNV[rPNb.mnI];
180 :
181 1 : ::std::swap(rVNa.maNext, rVNb.maNext);
182 : }
183 :
184 699 : if(!mbChanged)
185 : {
186 186 : mbChanged = true;
187 : }
188 699 : }
189 :
190 42 : B2DCubicBezier createSegment(const PN& rPN, bool bPrev) const
191 : {
192 42 : const B2DPoint& rStart(rPN.maPoint);
193 42 : const B2DPoint& rEnd(maPNV[bPrev ? rPN.mnIP : rPN.mnIN].maPoint);
194 42 : const B2DVector& rCPA(bPrev ? maVNV[rPN.mnI].maPrev : maVNV[rPN.mnI].maNext);
195 : // Use maOriginalNext, not maNext to create the original (yet unchanged)
196 : // curve segment. Otherwise, this segment would NOT ne correct.
197 42 : const B2DVector& rCPB(bPrev ? maVNV[maPNV[rPN.mnIP].mnI].maOriginalNext : maVNV[maPNV[rPN.mnIN].mnI].maPrev);
198 :
199 42 : return B2DCubicBezier(rStart, rStart + rCPA, rEnd + rCPB, rEnd);
200 : }
201 :
202 768 : void impHandleCommon(PN& rPNa, PN& rPNb)
203 : {
204 768 : if(mbIsCurve)
205 : {
206 8 : const B2DCubicBezier aNextA(createSegment(rPNa, false));
207 8 : const B2DCubicBezier aPrevA(createSegment(rPNa, true));
208 :
209 8 : if(aNextA.equal(aPrevA))
210 : {
211 : // deadend on A (identical edge)
212 : return;
213 : }
214 :
215 8 : const B2DCubicBezier aNextB(createSegment(rPNb, false));
216 8 : const B2DCubicBezier aPrevB(createSegment(rPNb, true));
217 :
218 8 : if(aNextB.equal(aPrevB))
219 : {
220 : // deadend on B (identical edge)
221 : return;
222 : }
223 :
224 8 : if(aPrevA.equal(aPrevB))
225 : {
226 : // common edge in same direction
227 : return;
228 : }
229 2 : else if(aPrevA.equal(aNextB))
230 : {
231 : // common edge in opposite direction
232 1 : if(aNextA.equal(aPrevB))
233 : {
234 : // common edge in opposite direction continues
235 : return;
236 : }
237 : else
238 : {
239 : // common edge in opposite direction leave
240 1 : impSwitchNext(rPNa, rPNb);
241 : }
242 : }
243 1 : else if(aNextA.equal(aNextB))
244 : {
245 : // common edge in same direction enter
246 : // search leave edge
247 1 : PN* pPNa2 = &maPNV[rPNa.mnIN];
248 1 : PN* pPNb2 = &maPNV[rPNb.mnIN];
249 1 : bool bOnEdge(true);
250 :
251 5 : do
252 : {
253 5 : const B2DCubicBezier aNextA2(createSegment(*pPNa2, false));
254 5 : const B2DCubicBezier aNextB2(createSegment(*pPNb2, false));
255 :
256 5 : if(aNextA2.equal(aNextB2))
257 : {
258 5 : pPNa2 = &maPNV[pPNa2->mnIN];
259 5 : pPNb2 = &maPNV[pPNb2->mnIN];
260 : }
261 : else
262 : {
263 0 : bOnEdge = false;
264 5 : }
265 : }
266 : while(bOnEdge && pPNa2 != &rPNa && pPNb2 != &rPNb);
267 :
268 1 : if(bOnEdge)
269 : {
270 : // loop over two identical polygon paths
271 : return;
272 : }
273 : else
274 : {
275 : // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
276 : // at enter/leave. Check for crossover.
277 0 : const B2DVector aPrevCA(aPrevA.interpolatePoint(0.5) - aPrevA.getStartPoint());
278 0 : const B2DVector aNextCA(aNextA.interpolatePoint(0.5) - aNextA.getStartPoint());
279 0 : const B2DVector aPrevCB(aPrevB.interpolatePoint(0.5) - aPrevB.getStartPoint());
280 0 : const bool bEnter(impLeftOfEdges(aPrevCA, aNextCA, aPrevCB));
281 :
282 0 : const B2DCubicBezier aNextA2(createSegment(*pPNa2, false));
283 0 : const B2DCubicBezier aPrevA2(createSegment(*pPNa2, true));
284 0 : const B2DCubicBezier aNextB2(createSegment(*pPNb2, false));
285 0 : const B2DVector aPrevCA2(aPrevA2.interpolatePoint(0.5) - aPrevA2.getStartPoint());
286 0 : const B2DVector aNextCA2(aNextA2.interpolatePoint(0.5) - aNextA2.getStartPoint());
287 0 : const B2DVector aNextCB2(aNextB2.interpolatePoint(0.5) - aNextB2.getStartPoint());
288 0 : const bool bLeave(impLeftOfEdges(aPrevCA2, aNextCA2, aNextCB2));
289 :
290 0 : if(bEnter != bLeave)
291 : {
292 : // crossover
293 0 : impSwitchNext(rPNa, rPNb);
294 0 : }
295 : }
296 : }
297 0 : else if(aNextA.equal(aPrevB))
298 : {
299 : // common edge in opposite direction enter
300 0 : impSwitchNext(rPNa, rPNb);
301 : }
302 : else
303 : {
304 : // no common edges, check for crossover
305 0 : const B2DVector aPrevCA(aPrevA.interpolatePoint(0.5) - aPrevA.getStartPoint());
306 0 : const B2DVector aNextCA(aNextA.interpolatePoint(0.5) - aNextA.getStartPoint());
307 0 : const B2DVector aPrevCB(aPrevB.interpolatePoint(0.5) - aPrevB.getStartPoint());
308 0 : const B2DVector aNextCB(aNextB.interpolatePoint(0.5) - aNextB.getStartPoint());
309 :
310 0 : const bool bEnter(impLeftOfEdges(aPrevCA, aNextCA, aPrevCB));
311 0 : const bool bLeave(impLeftOfEdges(aPrevCA, aNextCA, aNextCB));
312 :
313 0 : if(bEnter != bLeave)
314 : {
315 : // crossover
316 0 : impSwitchNext(rPNa, rPNb);
317 0 : }
318 8 : }
319 : }
320 : else
321 : {
322 760 : const B2DPoint& rNextA(maPNV[rPNa.mnIN].maPoint);
323 760 : const B2DPoint& rPrevA(maPNV[rPNa.mnIP].maPoint);
324 :
325 760 : if(rNextA.equal(rPrevA))
326 : {
327 : // deadend on A
328 0 : return;
329 : }
330 :
331 760 : const B2DPoint& rNextB(maPNV[rPNb.mnIN].maPoint);
332 760 : const B2DPoint& rPrevB(maPNV[rPNb.mnIP].maPoint);
333 :
334 760 : if(rNextB.equal(rPrevB))
335 : {
336 : // deadend on B
337 0 : return;
338 : }
339 :
340 760 : if(rPrevA.equal(rPrevB))
341 : {
342 : // common edge in same direction
343 51 : return;
344 : }
345 709 : else if(rPrevA.equal(rNextB))
346 : {
347 : // common edge in opposite direction
348 8 : if(rNextA.equal(rPrevB))
349 : {
350 : // common edge in opposite direction continues
351 5 : return;
352 : }
353 : else
354 : {
355 : // common edge in opposite direction leave
356 3 : impSwitchNext(rPNa, rPNb);
357 : }
358 : }
359 701 : else if(rNextA.equal(rNextB))
360 : {
361 : // common edge in same direction enter
362 : // search leave edge
363 16 : PN* pPNa2 = &maPNV[rPNa.mnIN];
364 16 : PN* pPNb2 = &maPNV[rPNb.mnIN];
365 16 : bool bOnEdge(true);
366 :
367 26 : do
368 : {
369 26 : const B2DPoint& rNextA2(maPNV[pPNa2->mnIN].maPoint);
370 26 : const B2DPoint& rNextB2(maPNV[pPNb2->mnIN].maPoint);
371 :
372 26 : if(rNextA2.equal(rNextB2))
373 : {
374 11 : pPNa2 = &maPNV[pPNa2->mnIN];
375 11 : pPNb2 = &maPNV[pPNb2->mnIN];
376 : }
377 : else
378 : {
379 15 : bOnEdge = false;
380 : }
381 : }
382 : while(bOnEdge && pPNa2 != &rPNa && pPNb2 != &rPNb);
383 :
384 16 : if(bOnEdge)
385 : {
386 : // loop over two identical polygon paths
387 1 : return;
388 : }
389 : else
390 : {
391 : // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
392 : // at enter/leave. Check for crossover.
393 15 : const B2DPoint& aPointE(rPNa.maPoint);
394 15 : const B2DVector aPrevAE(rPrevA - aPointE);
395 15 : const B2DVector aNextAE(rNextA - aPointE);
396 15 : const B2DVector aPrevBE(rPrevB - aPointE);
397 :
398 15 : const B2DPoint& aPointL(pPNa2->maPoint);
399 15 : const B2DVector aPrevAL(maPNV[pPNa2->mnIP].maPoint - aPointL);
400 15 : const B2DVector aNextAL(maPNV[pPNa2->mnIN].maPoint - aPointL);
401 15 : const B2DVector aNextBL(maPNV[pPNb2->mnIN].maPoint - aPointL);
402 :
403 15 : const bool bEnter(impLeftOfEdges(aPrevAE, aNextAE, aPrevBE));
404 15 : const bool bLeave(impLeftOfEdges(aPrevAL, aNextAL, aNextBL));
405 :
406 15 : if(bEnter != bLeave)
407 : {
408 : // crossover; switch start or end
409 14 : impSwitchNext(rPNa, rPNb);
410 15 : }
411 : }
412 : }
413 685 : else if(rNextA.equal(rPrevB))
414 : {
415 : // common edge in opposite direction enter
416 2 : impSwitchNext(rPNa, rPNb);
417 : }
418 : else
419 : {
420 : // no common edges, check for crossover
421 683 : const B2DPoint& aPoint(rPNa.maPoint);
422 683 : const B2DVector aPrevA(rPrevA - aPoint);
423 683 : const B2DVector aNextA(rNextA - aPoint);
424 683 : const B2DVector aPrevB(rPrevB - aPoint);
425 683 : const B2DVector aNextB(rNextB - aPoint);
426 :
427 683 : const bool bEnter(impLeftOfEdges(aPrevA, aNextA, aPrevB));
428 683 : const bool bLeave(impLeftOfEdges(aPrevA, aNextA, aNextB));
429 :
430 683 : if(bEnter != bLeave)
431 : {
432 : // crossover
433 679 : impSwitchNext(rPNa, rPNb);
434 683 : }
435 : }
436 : }
437 : }
438 :
439 205 : void impSolve()
440 : {
441 : // sort by point to identify common nodes
442 205 : ::std::sort(maSNV.begin(), maSNV.end());
443 :
444 : // handle common nodes
445 205 : const sal_uInt32 nNodeCount(maSNV.size());
446 :
447 3429 : for(sal_uInt32 a(0); a < nNodeCount - 1; a++)
448 : {
449 : // test a before using it, not after. Also use nPointCount instead of aSortNodes.size()
450 3224 : PN& rPNb = *(maSNV[a].mpPN);
451 :
452 3992 : for(sal_uInt32 b(a + 1); b < nNodeCount && rPNb.maPoint.equal(maSNV[b].mpPN->maPoint); b++)
453 : {
454 768 : impHandleCommon(rPNb, *maSNV[b].mpPN);
455 : }
456 : }
457 205 : }
458 :
459 : public:
460 8 : explicit solver(const B2DPolygon& rOriginal)
461 : : maOriginal(B2DPolyPolygon(rOriginal)),
462 : mbIsCurve(false),
463 8 : mbChanged(false)
464 : {
465 8 : const sal_uInt32 nOriginalCount(rOriginal.count());
466 :
467 8 : if(nOriginalCount)
468 : {
469 8 : B2DPolygon aGeometry(tools::addPointsAtCutsAndTouches(rOriginal));
470 8 : aGeometry.removeDoublePoints();
471 8 : aGeometry = tools::simplifyCurveSegments(aGeometry);
472 8 : mbIsCurve = aGeometry.areControlPointsUsed();
473 :
474 8 : const sal_uInt32 nPointCount(aGeometry.count());
475 :
476 : // If it's not a pezier polygon, at least four points are needed to create
477 : // a self-intersection. If it's a bezier polygon, the minimum point number
478 : // is two, since with a single point You get a curve, but no self-intersection
479 8 : if(nPointCount > 3 || (nPointCount > 1 && mbIsCurve))
480 : {
481 : // reserve space in point, control and sort vector.
482 8 : maSNV.reserve(nPointCount);
483 8 : maPNV.reserve(nPointCount);
484 8 : maVNV.reserve(mbIsCurve ? nPointCount : 0);
485 :
486 : // fill data
487 8 : impAddPolygon(0, aGeometry);
488 :
489 : // solve common nodes
490 8 : impSolve();
491 8 : }
492 : }
493 8 : }
494 :
495 197 : explicit solver(const B2DPolyPolygon& rOriginal)
496 : : maOriginal(rOriginal),
497 : mbIsCurve(false),
498 197 : mbChanged(false)
499 : {
500 197 : sal_uInt32 nOriginalCount(maOriginal.count());
501 :
502 197 : if(nOriginalCount)
503 : {
504 197 : B2DPolyPolygon aGeometry(tools::addPointsAtCutsAndTouches(maOriginal, true));
505 197 : aGeometry.removeDoublePoints();
506 197 : aGeometry = tools::simplifyCurveSegments(aGeometry);
507 197 : mbIsCurve = aGeometry.areControlPointsUsed();
508 197 : nOriginalCount = aGeometry.count();
509 :
510 197 : if(nOriginalCount)
511 : {
512 197 : sal_uInt32 nPointCount(0);
513 197 : sal_uInt32 a(0);
514 :
515 : // count points
516 593 : for(a = 0; a < nOriginalCount; a++)
517 : {
518 396 : const B2DPolygon aCandidate(aGeometry.getB2DPolygon(a));
519 396 : const sal_uInt32 nCandCount(aCandidate.count());
520 :
521 : // If it's not a bezier curve, at least three points would be needed to have a
522 : // topological relevant (not empty) polygon. Since its not known here if trivial
523 : // edges (dead ends) will be kept or sorted out, add non-bezier polygons with
524 : // more than one point.
525 : // For bezier curves, the minimum for defining an area is also one.
526 396 : if(nCandCount)
527 : {
528 396 : nPointCount += nCandCount;
529 : }
530 396 : }
531 :
532 197 : if(nPointCount)
533 : {
534 : // reserve space in point, control and sort vector.
535 197 : maSNV.reserve(nPointCount);
536 197 : maPNV.reserve(nPointCount);
537 197 : maVNV.reserve(mbIsCurve ? nPointCount : 0);
538 :
539 : // fill data
540 197 : sal_uInt32 nInsertIndex(0);
541 :
542 593 : for(a = 0; a < nOriginalCount; a++)
543 : {
544 396 : const B2DPolygon aCandidate(aGeometry.getB2DPolygon(a));
545 396 : const sal_uInt32 nCandCount(aCandidate.count());
546 :
547 : // use same condition as above, the data vector is
548 : // pre-allocated
549 396 : if(nCandCount)
550 : {
551 396 : impAddPolygon(nInsertIndex, aCandidate);
552 396 : nInsertIndex += nCandCount;
553 : }
554 396 : }
555 :
556 : // solve common nodes
557 197 : impSolve();
558 : }
559 197 : }
560 : }
561 197 : }
562 :
563 205 : B2DPolyPolygon getB2DPolyPolygon()
564 : {
565 205 : if(mbChanged)
566 : {
567 186 : B2DPolyPolygon aRetval;
568 186 : const sal_uInt32 nCount(maPNV.size());
569 186 : sal_uInt32 nCountdown(nCount);
570 :
571 1009 : for(sal_uInt32 a(0); nCountdown && a < nCount; a++)
572 : {
573 823 : PN& rPN = maPNV[a];
574 :
575 823 : if(SAL_MAX_UINT32 != rPN.mnI)
576 : {
577 : // unused node, start new part polygon
578 384 : B2DPolygon aNewPart;
579 384 : PN* pPNCurr = &rPN;
580 :
581 3277 : do
582 : {
583 3277 : const B2DPoint& rPoint = pPNCurr->maPoint;
584 3277 : aNewPart.append(rPoint);
585 :
586 3277 : if(mbIsCurve)
587 : {
588 14 : const VN& rVNCurr = maVNV[pPNCurr->mnI];
589 :
590 14 : if(!rVNCurr.maPrev.equalZero())
591 : {
592 2 : aNewPart.setPrevControlPoint(aNewPart.count() - 1, rPoint + rVNCurr.maPrev);
593 : }
594 :
595 14 : if(!rVNCurr.maNext.equalZero())
596 : {
597 2 : aNewPart.setNextControlPoint(aNewPart.count() - 1, rPoint + rVNCurr.maNext);
598 : }
599 : }
600 :
601 3277 : pPNCurr->mnI = SAL_MAX_UINT32;
602 3277 : nCountdown--;
603 3277 : pPNCurr = &(maPNV[pPNCurr->mnIN]);
604 : }
605 : while(pPNCurr != &rPN && SAL_MAX_UINT32 != pPNCurr->mnI);
606 :
607 : // close and add
608 384 : aNewPart.setClosed(true);
609 384 : aRetval.append(aNewPart);
610 : }
611 : }
612 :
613 186 : return aRetval;
614 : }
615 : else
616 : {
617 : // no change, return original
618 19 : return maOriginal;
619 : }
620 : }
621 : };
622 :
623 : //////////////////////////////////////////////////////////////////////////////
624 :
625 : } // end of anonymous namespace
626 : } // end of namespace basegfx
627 :
628 : //////////////////////////////////////////////////////////////////////////////
629 :
630 : namespace basegfx
631 : {
632 : namespace tools
633 : {
634 : //////////////////////////////////////////////////////////////////////////////
635 :
636 525 : B2DPolyPolygon solveCrossovers(const B2DPolyPolygon& rCandidate)
637 : {
638 525 : if(rCandidate.count() > 1L)
639 : {
640 191 : solver aSolver(rCandidate);
641 191 : return aSolver.getB2DPolyPolygon();
642 : }
643 : else
644 : {
645 334 : return rCandidate;
646 : }
647 : }
648 :
649 : //////////////////////////////////////////////////////////////////////////////
650 :
651 0 : B2DPolyPolygon solveCrossovers(const B2DPolygon& rCandidate)
652 : {
653 0 : solver aSolver(rCandidate);
654 0 : return aSolver.getB2DPolyPolygon();
655 : }
656 :
657 : //////////////////////////////////////////////////////////////////////////////
658 :
659 586 : B2DPolyPolygon stripNeutralPolygons(const B2DPolyPolygon& rCandidate)
660 : {
661 586 : B2DPolyPolygon aRetval;
662 :
663 1564 : for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
664 : {
665 978 : const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
666 :
667 978 : if(ORIENTATION_NEUTRAL != tools::getOrientation(aCandidate))
668 : {
669 965 : aRetval.append(aCandidate);
670 : }
671 978 : }
672 :
673 586 : return aRetval;
674 : }
675 :
676 : //////////////////////////////////////////////////////////////////////////////
677 :
678 0 : B2DPolyPolygon createNonzeroConform(const B2DPolyPolygon& rCandidate)
679 : {
680 0 : B2DPolyPolygon aCandidate;
681 :
682 : // remove all self-intersections and intersections
683 0 : if(rCandidate.count() == 1)
684 : {
685 0 : aCandidate = basegfx::tools::solveCrossovers(rCandidate.getB2DPolygon(0));
686 : }
687 : else
688 : {
689 0 : aCandidate = basegfx::tools::solveCrossovers(rCandidate);
690 : }
691 :
692 : // cleanup evtl. neutral polygons
693 0 : aCandidate = basegfx::tools::stripNeutralPolygons(aCandidate);
694 :
695 : // remove all polygons which have the same orientation as the polygon they are directly contained in
696 0 : const sal_uInt32 nCount(aCandidate.count());
697 :
698 0 : if(nCount > 1)
699 : {
700 : sal_uInt32 a, b;
701 0 : ::std::vector< StripHelper > aHelpers;
702 0 : aHelpers.resize(nCount);
703 :
704 0 : for(a = 0; a < nCount; a++)
705 : {
706 0 : const B2DPolygon aCand(aCandidate.getB2DPolygon(a));
707 0 : StripHelper* pNewHelper = &(aHelpers[a]);
708 0 : pNewHelper->maRange = tools::getRange(aCand);
709 0 : pNewHelper->meOrinetation = tools::getOrientation(aCand);
710 :
711 : // initialize with own orientation
712 0 : pNewHelper->mnDepth = (ORIENTATION_NEGATIVE == pNewHelper->meOrinetation ? -1 : 1);
713 0 : }
714 :
715 0 : for(a = 0; a < nCount - 1; a++)
716 : {
717 0 : const B2DPolygon aCandA(aCandidate.getB2DPolygon(a));
718 0 : StripHelper& rHelperA = aHelpers[a];
719 :
720 0 : for(b = a + 1; b < nCount; b++)
721 : {
722 0 : const B2DPolygon aCandB(aCandidate.getB2DPolygon(b));
723 0 : StripHelper& rHelperB = aHelpers[b];
724 0 : const bool bAInB(rHelperB.maRange.isInside(rHelperA.maRange) && tools::isInside(aCandB, aCandA, true));
725 :
726 0 : if(bAInB)
727 : {
728 : // A is inside B, add orientation of B to A
729 0 : rHelperA.mnDepth += (ORIENTATION_NEGATIVE == rHelperB.meOrinetation ? -1 : 1);
730 : }
731 :
732 0 : const bool bBInA(rHelperA.maRange.isInside(rHelperB.maRange) && tools::isInside(aCandA, aCandB, true));
733 :
734 0 : if(bBInA)
735 : {
736 : // B is inside A, add orientation of A to B
737 0 : rHelperB.mnDepth += (ORIENTATION_NEGATIVE == rHelperA.meOrinetation ? -1 : 1);
738 : }
739 0 : }
740 0 : }
741 :
742 0 : const B2DPolyPolygon aSource(aCandidate);
743 0 : aCandidate.clear();
744 :
745 0 : for(a = 0L; a < nCount; a++)
746 : {
747 0 : const StripHelper& rHelper = aHelpers[a];
748 : // for contained unequal oriented polygons sum will be 0
749 : // for contained equal it will be >=2 or <=-2
750 : // for free polygons (not contained) it will be 1 or -1
751 : // -> accept all which are >=-1 && <= 1
752 0 : bool bAcceptEntry(rHelper.mnDepth >= -1 && rHelper.mnDepth <= 1);
753 :
754 0 : if(bAcceptEntry)
755 : {
756 0 : aCandidate.append(aSource.getB2DPolygon(a));
757 : }
758 0 : }
759 : }
760 :
761 0 : return aCandidate;
762 : }
763 :
764 : //////////////////////////////////////////////////////////////////////////////
765 :
766 179 : B2DPolyPolygon stripDispensablePolygons(const B2DPolyPolygon& rCandidate, bool bKeepAboveZero)
767 : {
768 179 : const sal_uInt32 nCount(rCandidate.count());
769 179 : B2DPolyPolygon aRetval;
770 :
771 179 : if(nCount)
772 : {
773 179 : if(nCount == 1L)
774 : {
775 0 : if(!bKeepAboveZero && ORIENTATION_POSITIVE == tools::getOrientation(rCandidate.getB2DPolygon(0L)))
776 : {
777 0 : aRetval = rCandidate;
778 : }
779 : }
780 : else
781 : {
782 : sal_uInt32 a, b;
783 179 : ::std::vector< StripHelper > aHelpers;
784 179 : aHelpers.resize(nCount);
785 :
786 553 : for(a = 0L; a < nCount; a++)
787 : {
788 374 : const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
789 374 : StripHelper* pNewHelper = &(aHelpers[a]);
790 374 : pNewHelper->maRange = tools::getRange(aCandidate);
791 374 : pNewHelper->meOrinetation = tools::getOrientation(aCandidate);
792 374 : pNewHelper->mnDepth = (ORIENTATION_NEGATIVE == pNewHelper->meOrinetation ? -1L : 0L);
793 374 : }
794 :
795 374 : for(a = 0L; a < nCount - 1L; a++)
796 : {
797 195 : const B2DPolygon aCandA(rCandidate.getB2DPolygon(a));
798 195 : StripHelper& rHelperA = aHelpers[a];
799 :
800 417 : for(b = a + 1L; b < nCount; b++)
801 : {
802 222 : const B2DPolygon aCandB(rCandidate.getB2DPolygon(b));
803 222 : StripHelper& rHelperB = aHelpers[b];
804 222 : const bool bAInB(rHelperB.maRange.isInside(rHelperA.maRange) && tools::isInside(aCandB, aCandA, true));
805 222 : const bool bBInA(rHelperA.maRange.isInside(rHelperB.maRange) && tools::isInside(aCandA, aCandB, true));
806 :
807 222 : if(bAInB && bBInA)
808 : {
809 : // congruent
810 10 : if(rHelperA.meOrinetation == rHelperB.meOrinetation)
811 : {
812 : // two polys or two holes. Lower one of them to get one of them out of the way.
813 : // Since each will be contained in the other one, both will be increased, too.
814 : // So, for lowering, increase only one of them
815 5 : rHelperA.mnDepth++;
816 : }
817 : else
818 : {
819 : // poly and hole. They neutralize, so get rid of both. Move securely below zero.
820 0 : rHelperA.mnDepth = -((sal_Int32)nCount);
821 0 : rHelperB.mnDepth = -((sal_Int32)nCount);
822 : }
823 : }
824 : else
825 : {
826 217 : if(bAInB)
827 : {
828 9 : if(ORIENTATION_NEGATIVE == rHelperB.meOrinetation)
829 : {
830 0 : rHelperA.mnDepth--;
831 : }
832 : else
833 : {
834 9 : rHelperA.mnDepth++;
835 : }
836 : }
837 208 : else if(bBInA)
838 : {
839 170 : if(ORIENTATION_NEGATIVE == rHelperA.meOrinetation)
840 : {
841 0 : rHelperB.mnDepth--;
842 : }
843 : else
844 : {
845 170 : rHelperB.mnDepth++;
846 : }
847 : }
848 : }
849 222 : }
850 195 : }
851 :
852 553 : for(a = 0L; a < nCount; a++)
853 : {
854 374 : const StripHelper& rHelper = aHelpers[a];
855 374 : bool bAcceptEntry(bKeepAboveZero ? 1L <= rHelper.mnDepth : 0L == rHelper.mnDepth);
856 :
857 374 : if(bAcceptEntry)
858 : {
859 188 : aRetval.append(rCandidate.getB2DPolygon(a));
860 : }
861 179 : }
862 : }
863 : }
864 :
865 179 : return aRetval;
866 : }
867 :
868 : //////////////////////////////////////////////////////////////////////////////
869 :
870 8 : B2DPolyPolygon prepareForPolygonOperation(const B2DPolygon& rCandidate)
871 : {
872 8 : solver aSolver(rCandidate);
873 8 : B2DPolyPolygon aRetval(stripNeutralPolygons(aSolver.getB2DPolyPolygon()));
874 :
875 8 : return correctOrientations(aRetval);
876 : }
877 :
878 6 : B2DPolyPolygon prepareForPolygonOperation(const B2DPolyPolygon& rCandidate)
879 : {
880 6 : solver aSolver(rCandidate);
881 6 : B2DPolyPolygon aRetval(stripNeutralPolygons(aSolver.getB2DPolyPolygon()));
882 :
883 6 : return correctOrientations(aRetval);
884 : }
885 :
886 1 : B2DPolyPolygon solvePolygonOperationOr(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
887 : {
888 1 : if(!rCandidateA.count())
889 : {
890 0 : return rCandidateB;
891 : }
892 1 : else if(!rCandidateB.count())
893 : {
894 0 : return rCandidateA;
895 : }
896 : else
897 : {
898 : // concatenate polygons, solve crossovers and throw away all sub-polygons
899 : // which have a depth other than 0.
900 1 : B2DPolyPolygon aRetval(rCandidateA);
901 :
902 1 : aRetval.append(rCandidateB);
903 1 : aRetval = solveCrossovers(aRetval);
904 1 : aRetval = stripNeutralPolygons(aRetval);
905 :
906 1 : return stripDispensablePolygons(aRetval, false);
907 : }
908 : }
909 :
910 2 : B2DPolyPolygon solvePolygonOperationXor(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
911 : {
912 2 : if(!rCandidateA.count())
913 : {
914 0 : return rCandidateB;
915 : }
916 2 : else if(!rCandidateB.count())
917 : {
918 0 : return rCandidateA;
919 : }
920 : else
921 : {
922 : // XOR is pretty simple: By definition it is the simple concatenation of
923 : // the single polygons since we imply XOR fill rule. Make it intersection-free
924 : // and correct orientations
925 2 : B2DPolyPolygon aRetval(rCandidateA);
926 :
927 2 : aRetval.append(rCandidateB);
928 2 : aRetval = solveCrossovers(aRetval);
929 2 : aRetval = stripNeutralPolygons(aRetval);
930 :
931 2 : return correctOrientations(aRetval);
932 : }
933 : }
934 :
935 4 : B2DPolyPolygon solvePolygonOperationAnd(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
936 : {
937 4 : if(!rCandidateA.count())
938 : {
939 0 : return B2DPolyPolygon();
940 : }
941 4 : else if(!rCandidateB.count())
942 : {
943 0 : return B2DPolyPolygon();
944 : }
945 : else
946 : {
947 : // concatenate polygons, solve crossovers and throw away all sub-polygons
948 : // with a depth of < 1. This means to keep all polygons where at least two
949 : // polygons do overlap.
950 4 : B2DPolyPolygon aRetval(rCandidateA);
951 :
952 4 : aRetval.append(rCandidateB);
953 4 : aRetval = solveCrossovers(aRetval);
954 4 : aRetval = stripNeutralPolygons(aRetval);
955 :
956 4 : return stripDispensablePolygons(aRetval, true);
957 : }
958 : }
959 :
960 3 : B2DPolyPolygon solvePolygonOperationDiff(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
961 : {
962 3 : if(!rCandidateA.count())
963 : {
964 0 : return B2DPolyPolygon();
965 : }
966 3 : else if(!rCandidateB.count())
967 : {
968 0 : return rCandidateA;
969 : }
970 : else
971 : {
972 : // Make B topologically to holes and append to A
973 3 : B2DPolyPolygon aRetval(rCandidateB);
974 :
975 3 : aRetval.flip();
976 3 : aRetval.append(rCandidateA);
977 :
978 : // solve crossovers and throw away all sub-polygons which have a
979 : // depth other than 0.
980 3 : aRetval = basegfx::tools::solveCrossovers(aRetval);
981 3 : aRetval = basegfx::tools::stripNeutralPolygons(aRetval);
982 :
983 3 : return basegfx::tools::stripDispensablePolygons(aRetval, false);
984 : }
985 : }
986 :
987 0 : B2DPolyPolygon mergeToSinglePolyPolygon(const B2DPolyPolygonVector& rInput)
988 : {
989 0 : B2DPolyPolygonVector aInput(rInput);
990 :
991 : // first step: prepareForPolygonOperation and simple merge of non-overlapping
992 : // PolyPolygons for speedup; this is possible for the wanted OR-operation
993 0 : if(!aInput.empty())
994 : {
995 0 : B2DPolyPolygonVector aResult;
996 0 : aResult.reserve(aInput.size());
997 :
998 0 : for(sal_uInt32 a(0); a < aInput.size(); a++)
999 : {
1000 0 : const basegfx::B2DPolyPolygon aCandidate(prepareForPolygonOperation(aInput[a]));
1001 :
1002 0 : if(!aResult.empty())
1003 : {
1004 0 : const B2DRange aCandidateRange(aCandidate.getB2DRange());
1005 0 : bool bCouldMergeSimple(false);
1006 :
1007 0 : for(sal_uInt32 b(0); !bCouldMergeSimple && b < aResult.size(); b++)
1008 : {
1009 0 : basegfx::B2DPolyPolygon aTarget(aResult[b]);
1010 0 : const B2DRange aTargetRange(aTarget.getB2DRange());
1011 :
1012 0 : if(!aCandidateRange.overlaps(aTargetRange))
1013 : {
1014 0 : aTarget.append(aCandidate);
1015 0 : aResult[b] = aTarget;
1016 0 : bCouldMergeSimple = true;
1017 : }
1018 0 : }
1019 :
1020 0 : if(!bCouldMergeSimple)
1021 : {
1022 0 : aResult.push_back(aCandidate);
1023 : }
1024 : }
1025 : else
1026 : {
1027 0 : aResult.push_back(aCandidate);
1028 : }
1029 0 : }
1030 :
1031 0 : aInput = aResult;
1032 : }
1033 :
1034 : // second step: melt pairwise to a single PolyPolygon
1035 0 : while(aInput.size() > 1)
1036 : {
1037 0 : B2DPolyPolygonVector aResult;
1038 0 : aResult.reserve((aInput.size() / 2) + 1);
1039 :
1040 0 : for(sal_uInt32 a(0); a < aInput.size(); a += 2)
1041 : {
1042 0 : if(a + 1 < aInput.size())
1043 : {
1044 : // a pair for processing
1045 0 : aResult.push_back(solvePolygonOperationOr(aInput[a], aInput[a + 1]));
1046 : }
1047 : else
1048 : {
1049 : // last single PolyPolygon; copy to target to not lose it
1050 0 : aResult.push_back(aInput[a]);
1051 : }
1052 : }
1053 :
1054 0 : aInput = aResult;
1055 0 : }
1056 :
1057 : // third step: get result
1058 0 : if(1 == aInput.size())
1059 : {
1060 0 : return aInput[0];
1061 : }
1062 :
1063 0 : return B2DPolyPolygon();
1064 : }
1065 :
1066 : //////////////////////////////////////////////////////////////////////////////
1067 :
1068 : } // end of namespace tools
1069 : } // end of namespace basegfx
1070 :
1071 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|