xref: /AOO41X/main/basegfx/source/polygon/b2dpolygoncutandtouch.cxx (revision 09dbbe930366fe6f99ae3b8ae1cf8690b638dbda)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_basegfx.hxx"
26 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
27 #include <osl/diagnose.h>
28 #include <basegfx/numeric/ftools.hxx>
29 #include <basegfx/point/b2dpoint.hxx>
30 #include <basegfx/vector/b2dvector.hxx>
31 #include <basegfx/range/b2drange.hxx>
32 #include <basegfx/polygon/b2dpolygontools.hxx>
33 #include <basegfx/polygon/b2dpolypolygontools.hxx>
34 #include <basegfx/curve/b2dcubicbezier.hxx>
35 
36 #include <vector>
37 #include <algorithm>
38 
39 //////////////////////////////////////////////////////////////////////////////
40 // defines
41 
42 #define SUBDIVIDE_FOR_CUT_TEST_COUNT        (50)
43 
44 //////////////////////////////////////////////////////////////////////////////
45 
46 namespace basegfx
47 {
48     namespace
49     {
50         ////////////////////////////////////////////////////////////////////////////////
51 
52         class temporaryPoint
53         {
54             B2DPoint                            maPoint;        // the new point
55             sal_uInt32                          mnIndex;        // index after which to insert
56             double                              mfCut;          // parametric cut description [0.0 .. 1.0]
57 
58         public:
temporaryPoint(const B2DPoint & rNewPoint,sal_uInt32 nIndex,double fCut)59             temporaryPoint(const B2DPoint& rNewPoint, sal_uInt32 nIndex, double fCut)
60             :   maPoint(rNewPoint),
61                 mnIndex(nIndex),
62                 mfCut(fCut)
63             {
64             }
65 
operator <(const temporaryPoint & rComp) const66             bool operator<(const temporaryPoint& rComp) const
67             {
68                 if(mnIndex == rComp.mnIndex)
69                 {
70                     return (mfCut < rComp.mfCut);
71                 }
72 
73                 return (mnIndex < rComp.mnIndex);
74             }
75 
getPoint() const76             const B2DPoint& getPoint() const { return maPoint; }
getIndex() const77             sal_uInt32 getIndex() const { return mnIndex; }
getCut() const78             double getCut() const { return mfCut; }
79         };
80 
81         ////////////////////////////////////////////////////////////////////////////////
82 
83         typedef ::std::vector< temporaryPoint > temporaryPointVector;
84 
85         ////////////////////////////////////////////////////////////////////////////////
86 
87         class temporaryPolygonData
88         {
89             B2DPolygon                              maPolygon;
90             B2DRange                                maRange;
91             temporaryPointVector                    maPoints;
92 
93         public:
getPolygon() const94             const B2DPolygon& getPolygon() const { return maPolygon; }
setPolygon(const B2DPolygon & rNew)95             void setPolygon(const B2DPolygon& rNew) { maPolygon = rNew; maRange = tools::getRange(maPolygon); }
getRange() const96             const B2DRange& getRange() const { return maRange; }
getTemporaryPointVector()97             temporaryPointVector& getTemporaryPointVector() { return maPoints; }
98         };
99 
100         ////////////////////////////////////////////////////////////////////////////////
101 
mergeTemporaryPointsAndPolygon(const B2DPolygon & rCandidate,temporaryPointVector & rTempPoints)102         B2DPolygon mergeTemporaryPointsAndPolygon(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints)
103         {
104             // #i76891# mergeTemporaryPointsAndPolygon redesigned to be able to correctly handle
105             // single edges with/without control points
106             // #i101491# added counter for non-changing element count
107             const sal_uInt32 nTempPointCount(rTempPoints.size());
108 
109             if(nTempPointCount)
110             {
111                 B2DPolygon aRetval;
112                 const sal_uInt32 nCount(rCandidate.count());
113 
114                 if(nCount)
115                 {
116                     // sort temp points to assure increasing fCut values and increasing indices
117                     ::std::sort(rTempPoints.begin(), rTempPoints.end());
118 
119                     // prepare loop
120                     B2DCubicBezier aEdge;
121                     sal_uInt32 nNewInd(0L);
122 
123                     // add start point
124                     aRetval.append(rCandidate.getB2DPoint(0));
125 
126                     for(sal_uInt32 a(0L); a < nCount; a++)
127                     {
128                         // get edge
129                         rCandidate.getBezierSegment(a, aEdge);
130 
131                         if(aEdge.isBezier())
132                         {
133                             // control vectors involved for this edge
134                             double fLeftStart(0.0);
135 
136                             // now add all points targeted to be at this index
137                             while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a)
138                             {
139                                 const temporaryPoint& rTempPoint = rTempPoints[nNewInd++];
140 
141                                 // split curve segment. Splits need to come sorted and need to be < 1.0. Also,
142                                 // since original segment is consumed from left to right, the cut values need
143                                 // to be scaled to the remaining part
144                                 B2DCubicBezier aLeftPart;
145                                 const double fRelativeSplitPoint((rTempPoint.getCut() - fLeftStart) / (1.0 - fLeftStart));
146                                 aEdge.split(fRelativeSplitPoint, &aLeftPart, &aEdge);
147                                 fLeftStart = rTempPoint.getCut();
148 
149                                 // add left bow
150                                 aRetval.appendBezierSegment(aLeftPart.getControlPointA(), aLeftPart.getControlPointB(), rTempPoint.getPoint());
151                             }
152 
153                             // add remaining bow
154                             aRetval.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint());
155                         }
156                         else
157                         {
158                             // add all points targeted to be at this index
159                             while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a)
160                             {
161                                 const temporaryPoint& rTempPoint = rTempPoints[nNewInd++];
162                                 const B2DPoint aNewPoint(rTempPoint.getPoint());
163 
164                                 // do not add points double
165                                 if(!aRetval.getB2DPoint(aRetval.count() - 1L).equal(aNewPoint))
166                                 {
167                                     aRetval.append(aNewPoint);
168                                 }
169                             }
170 
171                             // add edge end point
172                             aRetval.append(aEdge.getEndPoint());
173                         }
174                     }
175                 }
176 
177                 if(rCandidate.isClosed())
178                 {
179                     // set closed flag and correct last point (which is added double now).
180                     tools::closeWithGeometryChange(aRetval);
181                 }
182 
183                 return aRetval;
184             }
185             else
186             {
187                 return rCandidate;
188             }
189         }
190 
191         ////////////////////////////////////////////////////////////////////////////////
192 
adaptAndTransferCutsWithBezierSegment(const temporaryPointVector & rPointVector,const B2DPolygon & rPolygon,sal_uInt32 nInd,temporaryPointVector & rTempPoints)193         void adaptAndTransferCutsWithBezierSegment(
194             const temporaryPointVector& rPointVector, const B2DPolygon& rPolygon,
195             sal_uInt32 nInd, temporaryPointVector& rTempPoints)
196         {
197             // assuming that the subdivision to create rPolygon used aequidistant pieces
198             // (as in adaptiveSubdivideByCount) it is now possible to calculate back the
199             // cut positions in the polygon to relative cut positions on the original bezier
200             // segment.
201             const sal_uInt32 nTempPointCount(rPointVector.size());
202             const sal_uInt32 nEdgeCount(rPolygon.count() ? rPolygon.count() - 1L : 0L);
203 
204             if(nTempPointCount && nEdgeCount)
205             {
206                 for(sal_uInt32 a(0L); a < nTempPointCount; a++)
207                 {
208                     const temporaryPoint& rTempPoint = rPointVector[a];
209                     const double fCutPosInPolygon((double)rTempPoint.getIndex() + rTempPoint.getCut());
210                     const double fRelativeCutPos(fCutPosInPolygon / (double)nEdgeCount);
211                     rTempPoints.push_back(temporaryPoint(rTempPoint.getPoint(), nInd, fRelativeCutPos));
212                 }
213             }
214         }
215 
216         ////////////////////////////////////////////////////////////////////////////////
217 
218     } // end of anonymous namespace
219 } // end of namespace basegfx
220 
221 //////////////////////////////////////////////////////////////////////////////
222 
223 namespace basegfx
224 {
225     namespace
226     {
227         ////////////////////////////////////////////////////////////////////////////////
228         // predefines for calls to this methods before method implementation
229 
230         void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints);
231         void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints);
232         void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB);
233 
234         ////////////////////////////////////////////////////////////////////////////////
235 
findEdgeCutsTwoEdges(const B2DPoint & rCurrA,const B2DPoint & rNextA,const B2DPoint & rCurrB,const B2DPoint & rNextB,sal_uInt32 nIndA,sal_uInt32 nIndB,temporaryPointVector & rTempPointsA,temporaryPointVector & rTempPointsB)236         void findEdgeCutsTwoEdges(
237             const B2DPoint& rCurrA, const B2DPoint& rNextA,
238             const B2DPoint& rCurrB, const B2DPoint& rNextB,
239             sal_uInt32 nIndA, sal_uInt32 nIndB,
240             temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
241         {
242             // no null length edges
243             if(!(rCurrA.equal(rNextA) || rCurrB.equal(rNextB)))
244             {
245                 // no common start/end points, this can be no cuts
246                 if(!(rCurrB.equal(rCurrA) || rCurrB.equal(rNextA) || rNextB.equal(rCurrA) || rNextB.equal(rNextA)))
247                 {
248                     const B2DVector aVecA(rNextA - rCurrA);
249                     const B2DVector aVecB(rNextB - rCurrB);
250                     double fCut(aVecA.cross(aVecB));
251 
252                     if(!fTools::equalZero(fCut))
253                     {
254                         const double fZero(0.0);
255                         const double fOne(1.0);
256                         fCut = (aVecB.getY() * (rCurrB.getX() - rCurrA.getX()) + aVecB.getX() * (rCurrA.getY() - rCurrB.getY())) / fCut;
257 
258                         if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne))
259                         {
260                             // it's a candidate, but also need to test parameter value of cut on line 2
261                             double fCut2;
262 
263                             // choose the more precise version
264                             if(fabs(aVecB.getX()) > fabs(aVecB.getY()))
265                             {
266                                 fCut2 = (rCurrA.getX() + (fCut * aVecA.getX()) - rCurrB.getX()) / aVecB.getX();
267                             }
268                             else
269                             {
270                                 fCut2 = (rCurrA.getY() + (fCut * aVecA.getY()) - rCurrB.getY()) / aVecB.getY();
271                             }
272 
273                             if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne))
274                             {
275                                 // cut is in range, add point. Two edges can have only one cut, but
276                                 // add a cut point to each list. The lists may be the same for
277                                 // self intersections.
278                                 const B2DPoint aCutPoint(interpolate(rCurrA, rNextA, fCut));
279                                 rTempPointsA.push_back(temporaryPoint(aCutPoint, nIndA, fCut));
280                                 rTempPointsB.push_back(temporaryPoint(aCutPoint, nIndB, fCut2));
281                             }
282                         }
283                     }
284                 }
285             }
286         }
287 
288         ////////////////////////////////////////////////////////////////////////////////
289 
findCutsAndTouchesAndCommonForBezier(const B2DPolygon & rCandidateA,const B2DPolygon & rCandidateB,temporaryPointVector & rTempPointsA,temporaryPointVector & rTempPointsB)290         void findCutsAndTouchesAndCommonForBezier(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
291         {
292             // #i76891#
293             // This new method is necessary since in findEdgeCutsBezierAndEdge and in findEdgeCutsTwoBeziers
294             // it is not sufficient to use findCuts() recursively. This will indeed find the cuts between the
295             // segments of the two temporarily adaptive subdivided bezier segments, but not the touches or
296             // equal points of them.
297             // It would be possible to find the toches using findTouches(), but at last with commpn points
298             // the adding of cut points (temporary points) would fail. But for these temporarily adaptive
299             // subdivided bezier segments, common points may be not very likely, but the bug shows that it
300             // happens.
301             // Touch points are a little bit more likely than common points. All in all it is best to use
302             // a specialized method here which can profit from knowing that it is working on a special
303             // family of B2DPolygons: no curve segments included and not closed.
304             OSL_ENSURE(!rCandidateA.areControlPointsUsed() && !rCandidateB.areControlPointsUsed(), "findCutsAndTouchesAndCommonForBezier only works with subdivided polygons (!)");
305             OSL_ENSURE(!rCandidateA.isClosed() && !rCandidateB.isClosed(), "findCutsAndTouchesAndCommonForBezier only works with opened polygons (!)");
306             const sal_uInt32 nPointCountA(rCandidateA.count());
307             const sal_uInt32 nPointCountB(rCandidateB.count());
308 
309             if(nPointCountA > 1 && nPointCountB > 1)
310             {
311                 const sal_uInt32 nEdgeCountA(nPointCountA - 1);
312                 const sal_uInt32 nEdgeCountB(nPointCountB - 1);
313                 B2DPoint aCurrA(rCandidateA.getB2DPoint(0L));
314 
315                 for(sal_uInt32 a(0L); a < nEdgeCountA; a++)
316                 {
317                     const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1L));
318                     const B2DRange aRangeA(aCurrA, aNextA);
319                     B2DPoint aCurrB(rCandidateB.getB2DPoint(0L));
320 
321                     for(sal_uInt32 b(0L); b < nEdgeCountB; b++)
322                     {
323                         const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1L));
324                         const B2DRange aRangeB(aCurrB, aNextB);
325 
326                         if(aRangeA.overlaps(aRangeB))
327                         {
328                             // no null length edges
329                             if(!(aCurrA.equal(aNextA) || aCurrB.equal(aNextB)))
330                             {
331                                 const B2DVector aVecA(aNextA - aCurrA);
332                                 const B2DVector aVecB(aNextB - aCurrB);
333                                 double fCutA(aVecA.cross(aVecB));
334 
335                                 if(!fTools::equalZero(fCutA))
336                                 {
337                                     const double fZero(0.0);
338                                     const double fOne(1.0);
339                                     fCutA = (aVecB.getY() * (aCurrB.getX() - aCurrA.getX()) + aVecB.getX() * (aCurrA.getY() - aCurrB.getY())) / fCutA;
340 
341                                     // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
342                                     // as 0.0 cut. The 1.0 cut will be registered in the next loop step
343                                     if(fTools::moreOrEqual(fCutA, fZero) && fTools::less(fCutA, fOne))
344                                     {
345                                         // it's a candidate, but also need to test parameter value of cut on line 2
346                                         double fCutB;
347 
348                                         // choose the more precise version
349                                         if(fabs(aVecB.getX()) > fabs(aVecB.getY()))
350                                         {
351                                             fCutB = (aCurrA.getX() + (fCutA * aVecA.getX()) - aCurrB.getX()) / aVecB.getX();
352                                         }
353                                         else
354                                         {
355                                             fCutB = (aCurrA.getY() + (fCutA * aVecA.getY()) - aCurrB.getY()) / aVecB.getY();
356                                         }
357 
358                                         // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
359                                         // as 0.0 cut. The 1.0 cut will be registered in the next loop step
360                                         if(fTools::moreOrEqual(fCutB, fZero) && fTools::less(fCutB, fOne))
361                                         {
362                                             // cut is in both ranges. Add points for A and B
363                                             // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy
364                                             if(fTools::equal(fCutA, fZero))
365                                             {
366                                                 // ignore for start point in first edge; this is handled
367                                                 // by outer methods and would just produce a double point
368                                                 if(a)
369                                                 {
370                                                     rTempPointsA.push_back(temporaryPoint(aCurrA, a, 0.0));
371                                                 }
372                                             }
373                                             else
374                                             {
375                                                 const B2DPoint aCutPoint(interpolate(aCurrA, aNextA, fCutA));
376                                                 rTempPointsA.push_back(temporaryPoint(aCutPoint, a, fCutA));
377                                             }
378 
379                                             // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy
380                                             if(fTools::equal(fCutB, fZero))
381                                             {
382                                                 // ignore for start point in first edge; this is handled
383                                                 // by outer methods and would just produce a double point
384                                                 if(b)
385                                                 {
386                                                     rTempPointsB.push_back(temporaryPoint(aCurrB, b, 0.0));
387                                                 }
388                                             }
389                                             else
390                                             {
391                                                 const B2DPoint aCutPoint(interpolate(aCurrB, aNextB, fCutB));
392                                                 rTempPointsB.push_back(temporaryPoint(aCutPoint, b, fCutB));
393                                             }
394                                         }
395                                     }
396                                 }
397                             }
398                         }
399 
400                         // prepare next step
401                         aCurrB = aNextB;
402                     }
403 
404                     // prepare next step
405                     aCurrA = aNextA;
406                 }
407             }
408         }
409 
410         ////////////////////////////////////////////////////////////////////////////////
411 
findEdgeCutsBezierAndEdge(const B2DCubicBezier & rCubicA,const B2DPoint & rCurrB,const B2DPoint & rNextB,sal_uInt32 nIndA,sal_uInt32 nIndB,temporaryPointVector & rTempPointsA,temporaryPointVector & rTempPointsB)412         void findEdgeCutsBezierAndEdge(
413             const B2DCubicBezier& rCubicA,
414             const B2DPoint& rCurrB, const B2DPoint& rNextB,
415             sal_uInt32 nIndA, sal_uInt32 nIndB,
416             temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
417         {
418             // find all cuts between given bezier segment and edge. Add an entry to the tempPoints
419             // for each common point with the cut value describing the relative position on given
420             // bezier segment and edge.
421             B2DPolygon aTempPolygonA;
422             B2DPolygon aTempPolygonEdge;
423             temporaryPointVector aTempPointVectorA;
424             temporaryPointVector aTempPointVectorEdge;
425 
426             // create subdivided polygons and find cuts between them
427             // Keep adaptiveSubdivideByCount due to needed quality
428             aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
429             aTempPolygonA.append(rCubicA.getStartPoint());
430             rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT);
431             aTempPolygonEdge.append(rCurrB);
432             aTempPolygonEdge.append(rNextB);
433 
434             // #i76891# using findCuts recursively is not sufficient here
435             findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonEdge, aTempPointVectorA, aTempPointVectorEdge);
436 
437             if(aTempPointVectorA.size())
438             {
439                 // adapt tempVector entries to segment
440                 adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA);
441             }
442 
443             // append remapped tempVector entries for edge to tempPoints for edge
444             for(sal_uInt32 a(0L); a < aTempPointVectorEdge.size(); a++)
445             {
446                 const temporaryPoint& rTempPoint = aTempPointVectorEdge[a];
447                 rTempPointsB.push_back(temporaryPoint(rTempPoint.getPoint(), nIndB, rTempPoint.getCut()));
448             }
449         }
450 
451         ////////////////////////////////////////////////////////////////////////////////
452 
findEdgeCutsTwoBeziers(const B2DCubicBezier & rCubicA,const B2DCubicBezier & rCubicB,sal_uInt32 nIndA,sal_uInt32 nIndB,temporaryPointVector & rTempPointsA,temporaryPointVector & rTempPointsB)453         void findEdgeCutsTwoBeziers(
454             const B2DCubicBezier& rCubicA,
455             const B2DCubicBezier& rCubicB,
456             sal_uInt32 nIndA, sal_uInt32 nIndB,
457             temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
458         {
459             // find all cuts between the two given bezier segments. Add an entry to the tempPoints
460             // for each common point with the cut value describing the relative position on given
461             // bezier segments.
462             B2DPolygon aTempPolygonA;
463             B2DPolygon aTempPolygonB;
464             temporaryPointVector aTempPointVectorA;
465             temporaryPointVector aTempPointVectorB;
466 
467             // create subdivided polygons and find cuts between them
468             // Keep adaptiveSubdivideByCount due to needed quality
469             aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
470             aTempPolygonA.append(rCubicA.getStartPoint());
471             rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT);
472             aTempPolygonB.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
473             aTempPolygonB.append(rCubicB.getStartPoint());
474             rCubicB.adaptiveSubdivideByCount(aTempPolygonB, SUBDIVIDE_FOR_CUT_TEST_COUNT);
475 
476             // #i76891# using findCuts recursively is not sufficient here
477             findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonB, aTempPointVectorA, aTempPointVectorB);
478 
479             if(aTempPointVectorA.size())
480             {
481                 // adapt tempVector entries to segment
482                 adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA);
483             }
484 
485             if(aTempPointVectorB.size())
486             {
487                 // adapt tempVector entries to segment
488                 adaptAndTransferCutsWithBezierSegment(aTempPointVectorB, aTempPolygonB, nIndB, rTempPointsB);
489             }
490         }
491 
492         ////////////////////////////////////////////////////////////////////////////////
493 
findEdgeCutsOneBezier(const B2DCubicBezier & rCubicA,sal_uInt32 nInd,temporaryPointVector & rTempPoints)494         void findEdgeCutsOneBezier(
495             const B2DCubicBezier& rCubicA,
496             sal_uInt32 nInd, temporaryPointVector& rTempPoints)
497         {
498             // avoid expensive part of this method if possible
499             // TODO: use hasAnyExtremum() method instead when it becomes available
500             double fDummy;
501             const bool bHasAnyExtremum = rCubicA.getMinimumExtremumPosition( fDummy );
502             if( !bHasAnyExtremum )
503                 return;
504 
505             // find all self-intersections on the given bezier segment. Add an entry to the tempPoints
506             // for each self intersection point with the cut value describing the relative position on given
507             // bezier segment.
508             B2DPolygon aTempPolygon;
509             temporaryPointVector aTempPointVector;
510 
511             // create subdivided polygon and find cuts on it
512             // Keep adaptiveSubdivideByCount due to needed quality
513             aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
514             aTempPolygon.append(rCubicA.getStartPoint());
515             rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT);
516             findCuts(aTempPolygon, aTempPointVector);
517 
518             if(aTempPointVector.size())
519             {
520                 // adapt tempVector entries to segment
521                 adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints);
522             }
523         }
524 
525         ////////////////////////////////////////////////////////////////////////////////
526 
findCuts(const B2DPolygon & rCandidate,temporaryPointVector & rTempPoints)527         void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints)
528         {
529             // find out if there are edges with intersections (self-cuts). If yes, add
530             // entries to rTempPoints accordingly
531             const sal_uInt32 nPointCount(rCandidate.count());
532 
533             if(nPointCount)
534             {
535                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
536 
537                 if(nEdgeCount)
538                 {
539                     const bool bCurvesInvolved(rCandidate.areControlPointsUsed());
540 
541                     if(bCurvesInvolved)
542                     {
543                         B2DCubicBezier aCubicA;
544                         B2DCubicBezier aCubicB;
545 
546                         for(sal_uInt32 a(0L); a < nEdgeCount - 1L; a++)
547                         {
548                             rCandidate.getBezierSegment(a, aCubicA);
549                             aCubicA.testAndSolveTrivialBezier();
550                             const bool bEdgeAIsCurve(aCubicA.isBezier());
551                             const B2DRange aRangeA(aCubicA.getRange());
552 
553                             if(bEdgeAIsCurve)
554                             {
555                                 // curved segments may have self-intersections, do not forget those (!)
556                                 findEdgeCutsOneBezier(aCubicA, a, rTempPoints);
557                             }
558 
559                             for(sal_uInt32 b(a + 1L); b < nEdgeCount; b++)
560                             {
561                                 rCandidate.getBezierSegment(b, aCubicB);
562                                 aCubicB.testAndSolveTrivialBezier();
563                                 const bool bEdgeBIsCurve(aCubicB.isBezier());
564                                 const B2DRange aRangeB(aCubicB.getRange());
565 
566                                 // only overlapping segments need to be tested
567                                 // consecutive segments touch of course
568                                 bool bOverlap = false;
569                                 if( b > a+1)
570                                     bOverlap = aRangeA.overlaps(aRangeB);
571                                 else
572                                     bOverlap = aRangeA.overlapsMore(aRangeB);
573                                 if( bOverlap)
574                                 {
575                                     if(bEdgeAIsCurve && bEdgeBIsCurve)
576                                     {
577                                         // test for bezier-bezier cuts
578                                         findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPoints, rTempPoints);
579                                     }
580                                     else if(bEdgeAIsCurve)
581                                     {
582                                         // test for bezier-edge cuts
583                                         findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPoints, rTempPoints);
584                                     }
585                                     else if(bEdgeBIsCurve)
586                                     {
587                                         // test for bezier-edge cuts
588                                         findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPoints, rTempPoints);
589                                     }
590                                     else
591                                     {
592                                         // test for simple edge-edge cuts
593                                         findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(),
594                                             a, b, rTempPoints, rTempPoints);
595                                     }
596                                 }
597                             }
598                         }
599                     }
600                     else
601                     {
602                         B2DPoint aCurrA(rCandidate.getB2DPoint(0L));
603 
604                         for(sal_uInt32 a(0L); a < nEdgeCount - 1L; a++)
605                         {
606                             const B2DPoint aNextA(rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L));
607                             const B2DRange aRangeA(aCurrA, aNextA);
608                             B2DPoint aCurrB(rCandidate.getB2DPoint(a + 1L));
609 
610                             for(sal_uInt32 b(a + 1L); b < nEdgeCount; b++)
611                             {
612                                 const B2DPoint aNextB(rCandidate.getB2DPoint(b + 1L == nPointCount ? 0L : b + 1L));
613                                 const B2DRange aRangeB(aCurrB, aNextB);
614 
615                                 // consecutive segments touch of course
616                                 bool bOverlap = false;
617                                 if( b > a+1)
618                                     bOverlap = aRangeA.overlaps(aRangeB);
619                                 else
620                                     bOverlap = aRangeA.overlapsMore(aRangeB);
621                                 if( bOverlap)
622                                 {
623                                     findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPoints, rTempPoints);
624                                 }
625 
626                                 // prepare next step
627                                 aCurrB = aNextB;
628                             }
629 
630                             // prepare next step
631                             aCurrA = aNextA;
632                         }
633                     }
634                 }
635             }
636         }
637 
638         ////////////////////////////////////////////////////////////////////////////////
639 
640     } // end of anonymous namespace
641 } // end of namespace basegfx
642 
643 //////////////////////////////////////////////////////////////////////////////
644 
645 namespace basegfx
646 {
647     namespace
648     {
649         ////////////////////////////////////////////////////////////////////////////////
650 
findTouchesOnEdge(const B2DPoint & rCurr,const B2DPoint & rNext,const B2DPolygon & rPointPolygon,sal_uInt32 nInd,temporaryPointVector & rTempPoints)651         void findTouchesOnEdge(
652             const B2DPoint& rCurr, const B2DPoint& rNext, const B2DPolygon& rPointPolygon,
653             sal_uInt32 nInd, temporaryPointVector& rTempPoints)
654         {
655             // find out if points from rPointPolygon are positioned on given edge. If Yes, add
656             // points there to represent touches (which may be enter or leave nodes later).
657             const sal_uInt32 nPointCount(rPointPolygon.count());
658 
659             if(nPointCount)
660             {
661                 const B2DRange aRange(rCurr, rNext);
662                 const B2DVector aEdgeVector(rNext - rCurr);
663                 B2DVector aNormalizedEdgeVector(aEdgeVector);
664                 aNormalizedEdgeVector.normalize();
665                 bool bTestUsingX(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY()));
666 
667                 for(sal_uInt32 a(0L); a < nPointCount; a++)
668                 {
669                     const B2DPoint aTestPoint(rPointPolygon.getB2DPoint(a));
670 
671                     if(aRange.isInside(aTestPoint))
672                     {
673                         if(!aTestPoint.equal(rCurr) && !aTestPoint.equal(rNext))
674                         {
675                             const B2DVector aTestVector(aTestPoint - rCurr);
676 
677                             if(areParallel(aNormalizedEdgeVector, aTestVector))
678                             {
679                                 const double fCut((bTestUsingX)
680                                     ? aTestVector.getX() / aEdgeVector.getX()
681                                     : aTestVector.getY() / aEdgeVector.getY());
682                                 const double fZero(0.0);
683                                 const double fOne(1.0);
684 
685                                 if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne))
686                                 {
687                                     rTempPoints.push_back(temporaryPoint(aTestPoint, nInd, fCut));
688                                 }
689                             }
690                         }
691                     }
692                 }
693             }
694         }
695 
696         ////////////////////////////////////////////////////////////////////////////////
697 
findTouchesOnCurve(const B2DCubicBezier & rCubicA,const B2DPolygon & rPointPolygon,sal_uInt32 nInd,temporaryPointVector & rTempPoints)698         void findTouchesOnCurve(
699             const B2DCubicBezier& rCubicA, const B2DPolygon& rPointPolygon,
700             sal_uInt32 nInd, temporaryPointVector& rTempPoints)
701         {
702             // find all points from rPointPolygon which touch the given bezier segment. Add an entry
703             // for each touch to the given pointVector. The cut for that entry is the relative position on
704             // the given bezier segment.
705             B2DPolygon aTempPolygon;
706             temporaryPointVector aTempPointVector;
707 
708             // create subdivided polygon and find cuts on it
709             // Keep adaptiveSubdivideByCount due to needed quality
710             aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
711             aTempPolygon.append(rCubicA.getStartPoint());
712             rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT);
713             findTouches(aTempPolygon, rPointPolygon, aTempPointVector);
714 
715             if(aTempPointVector.size())
716             {
717                 // adapt tempVector entries to segment
718                 adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints);
719             }
720         }
721 
722         ////////////////////////////////////////////////////////////////////////////////
723 
findTouches(const B2DPolygon & rEdgePolygon,const B2DPolygon & rPointPolygon,temporaryPointVector & rTempPoints)724         void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints)
725         {
726             // find out if points from rPointPolygon touch edges from rEdgePolygon. If yes,
727             // add entries to rTempPoints
728             const sal_uInt32 nPointCount(rPointPolygon.count());
729             const sal_uInt32 nEdgePointCount(rEdgePolygon.count());
730 
731             if(nPointCount && nEdgePointCount)
732             {
733                 const sal_uInt32 nEdgeCount(rEdgePolygon.isClosed() ? nEdgePointCount : nEdgePointCount - 1L);
734                 B2DPoint aCurr(rEdgePolygon.getB2DPoint(0));
735 
736                 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
737                 {
738                     const sal_uInt32 nNextIndex((a + 1) % nEdgePointCount);
739                     const B2DPoint aNext(rEdgePolygon.getB2DPoint(nNextIndex));
740 
741                     if(!aCurr.equal(aNext))
742                     {
743                         bool bHandleAsSimpleEdge(true);
744 
745                         if(rEdgePolygon.areControlPointsUsed())
746                         {
747                             const B2DPoint aNextControlPoint(rEdgePolygon.getNextControlPoint(a));
748                             const B2DPoint aPrevControlPoint(rEdgePolygon.getPrevControlPoint(nNextIndex));
749                             const bool bEdgeIsCurve(!aNextControlPoint.equal(aCurr) || !aPrevControlPoint.equal(aNext));
750 
751                             if(bEdgeIsCurve)
752                             {
753                                 bHandleAsSimpleEdge = false;
754                                 const B2DCubicBezier aCubicA(aCurr, aNextControlPoint, aPrevControlPoint, aNext);
755                                 findTouchesOnCurve(aCubicA, rPointPolygon, a, rTempPoints);
756                             }
757                         }
758 
759                         if(bHandleAsSimpleEdge)
760                         {
761                             findTouchesOnEdge(aCurr, aNext, rPointPolygon, a, rTempPoints);
762                         }
763                     }
764 
765                     // next step
766                     aCurr = aNext;
767                 }
768             }
769         }
770 
771         ////////////////////////////////////////////////////////////////////////////////
772 
773     } // end of anonymous namespace
774 } // end of namespace basegfx
775 
776 //////////////////////////////////////////////////////////////////////////////
777 
778 namespace basegfx
779 {
780     namespace
781     {
782         ////////////////////////////////////////////////////////////////////////////////
783 
findCuts(const B2DPolygon & rCandidateA,const B2DPolygon & rCandidateB,temporaryPointVector & rTempPointsA,temporaryPointVector & rTempPointsB)784         void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
785         {
786             // find out if edges from both polygons cut. If so, add entries to rTempPoints which
787             // should be added to the polygons accordingly
788             const sal_uInt32 nPointCountA(rCandidateA.count());
789             const sal_uInt32 nPointCountB(rCandidateB.count());
790 
791             if(nPointCountA && nPointCountB)
792             {
793                 const sal_uInt32 nEdgeCountA(rCandidateA.isClosed() ? nPointCountA : nPointCountA - 1L);
794                 const sal_uInt32 nEdgeCountB(rCandidateB.isClosed() ? nPointCountB : nPointCountB - 1L);
795 
796                 if(nEdgeCountA && nEdgeCountB)
797                 {
798                     const bool bCurvesInvolved(rCandidateA.areControlPointsUsed() || rCandidateB.areControlPointsUsed());
799 
800                     if(bCurvesInvolved)
801                     {
802                         B2DCubicBezier aCubicA;
803                         B2DCubicBezier aCubicB;
804 
805                         for(sal_uInt32 a(0L); a < nEdgeCountA; a++)
806                         {
807                             rCandidateA.getBezierSegment(a, aCubicA);
808                             aCubicA.testAndSolveTrivialBezier();
809                             const bool bEdgeAIsCurve(aCubicA.isBezier());
810                             const B2DRange aRangeA(aCubicA.getRange());
811 
812                             for(sal_uInt32 b(0L); b < nEdgeCountB; b++)
813                             {
814                                 rCandidateB.getBezierSegment(b, aCubicB);
815                                 aCubicB.testAndSolveTrivialBezier();
816                                 const bool bEdgeBIsCurve(aCubicB.isBezier());
817                                 const B2DRange aRangeB(aCubicB.getRange());
818 
819                                 // consecutive segments touch of course
820                                 bool bOverlap = false;
821                                 if( b > a+1)
822                                     bOverlap = aRangeA.overlaps(aRangeB);
823                                 else
824                                     bOverlap = aRangeA.overlapsMore(aRangeB);
825                                 if( bOverlap)
826                                 {
827                                     if(bEdgeAIsCurve && bEdgeBIsCurve)
828                                     {
829                                         // test for bezier-bezier cuts
830                                         findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPointsA, rTempPointsB);
831                                     }
832                                     else if(bEdgeAIsCurve)
833                                     {
834                                         // test for bezier-edge cuts
835                                         findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPointsA, rTempPointsB);
836                                     }
837                                     else if(bEdgeBIsCurve)
838                                     {
839                                         // test for bezier-edge cuts
840                                         findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPointsB, rTempPointsA);
841                                     }
842                                     else
843                                     {
844                                         // test for simple edge-edge cuts
845                                         findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(),
846                                             a, b, rTempPointsA, rTempPointsB);
847                                     }
848                                 }
849                             }
850                         }
851                     }
852                     else
853                     {
854                         B2DPoint aCurrA(rCandidateA.getB2DPoint(0L));
855 
856                         for(sal_uInt32 a(0L); a < nEdgeCountA; a++)
857                         {
858                             const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1L == nPointCountA ? 0L : a + 1L));
859                             const B2DRange aRangeA(aCurrA, aNextA);
860                             B2DPoint aCurrB(rCandidateB.getB2DPoint(0L));
861 
862                             for(sal_uInt32 b(0L); b < nEdgeCountB; b++)
863                             {
864                                 const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1L == nPointCountB ? 0L : b + 1L));
865                                 const B2DRange aRangeB(aCurrB, aNextB);
866 
867                                 // consecutive segments touch of course
868                                 bool bOverlap = false;
869                                 if( b > a+1)
870                                     bOverlap = aRangeA.overlaps(aRangeB);
871                                 else
872                                     bOverlap = aRangeA.overlapsMore(aRangeB);
873                                 if( bOverlap)
874                                 {
875                                     // test for simple edge-edge cuts
876                                     findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPointsA, rTempPointsB);
877                                 }
878 
879                                 // prepare next step
880                                 aCurrB = aNextB;
881                             }
882 
883                             // prepare next step
884                             aCurrA = aNextA;
885                         }
886                     }
887                 }
888             }
889         }
890 
891         ////////////////////////////////////////////////////////////////////////////////
892 
893     } // end of anonymous namespace
894 } // end of namespace basegfx
895 
896 //////////////////////////////////////////////////////////////////////////////
897 
898 namespace basegfx
899 {
900     namespace tools
901     {
902         ////////////////////////////////////////////////////////////////////////////////
903 
addPointsAtCutsAndTouches(const B2DPolygon & rCandidate)904         B2DPolygon addPointsAtCutsAndTouches(const B2DPolygon& rCandidate)
905         {
906             if(rCandidate.count())
907             {
908                 temporaryPointVector aTempPoints;
909 
910                 findTouches(rCandidate, rCandidate, aTempPoints);
911                 findCuts(rCandidate, aTempPoints);
912 
913                 return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
914             }
915             else
916             {
917                 return rCandidate;
918             }
919         }
920 
921         ////////////////////////////////////////////////////////////////////////////////
922 
addPointsAtCutsAndTouches(const B2DPolyPolygon & rCandidate,bool bSelfIntersections)923         B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rCandidate, bool bSelfIntersections)
924         {
925             const sal_uInt32 nCount(rCandidate.count());
926 
927             if(nCount)
928             {
929                 B2DPolyPolygon aRetval;
930 
931                 if(1L == nCount)
932                 {
933                     if(bSelfIntersections)
934                     {
935                         // remove self intersections
936                         aRetval.append(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(0L)));
937                     }
938                     else
939                     {
940                         // copy source
941                         aRetval = rCandidate;
942                     }
943                 }
944                 else
945                 {
946                     // first solve self cuts and self touches for all contained single polygons
947                     temporaryPolygonData *pTempData = new temporaryPolygonData[nCount];
948                     sal_uInt32 a, b;
949 
950                     for(a = 0L; a < nCount; a++)
951                     {
952                         if(bSelfIntersections)
953                         {
954                             // use polygons with solved self intersections
955                             pTempData[a].setPolygon(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(a)));
956                         }
957                         else
958                         {
959                             // copy given polygons
960                             pTempData[a].setPolygon(rCandidate.getB2DPolygon(a));
961                         }
962                     }
963 
964                     // now cuts and touches between the polygons
965                     for(a = 0L; a < nCount; a++)
966                     {
967                         for(b = 0L; b < nCount; b++)
968                         {
969                             if(a != b)
970                             {
971                                 // look for touches, compare each edge polygon to all other points
972                                 if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
973                                 {
974                                     findTouches(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector());
975                                 }
976                             }
977 
978                             if(a < b)
979                             {
980                                 // look for cuts, compare each edge polygon to following ones
981                                 if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
982                                 {
983                                     findCuts(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector(), pTempData[b].getTemporaryPointVector());
984                                 }
985                             }
986                         }
987                     }
988 
989                     // consolidate the result
990                     for(a = 0L; a < nCount; a++)
991                     {
992                         aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector()));
993                     }
994 
995                     delete[] pTempData;
996                 }
997 
998                 return aRetval;
999             }
1000             else
1001             {
1002                 return rCandidate;
1003             }
1004         }
1005 
1006         ////////////////////////////////////////////////////////////////////////////////
1007 
addPointsAtCutsAndTouches(const B2DPolyPolygon & rMask,const B2DPolygon & rCandidate)1008         B2DPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolygon& rCandidate)
1009         {
1010             if(rCandidate.count())
1011             {
1012                 temporaryPointVector aTempPoints;
1013                 temporaryPointVector aTempPointsUnused;
1014 
1015                 for(sal_uInt32 a(0L); a < rMask.count(); a++)
1016                 {
1017                     const B2DPolygon aPartMask(rMask.getB2DPolygon(a));
1018 
1019                     findTouches(rCandidate, aPartMask, aTempPoints);
1020                     findCuts(rCandidate, aPartMask, aTempPoints, aTempPointsUnused);
1021                 }
1022 
1023                 return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
1024             }
1025             else
1026             {
1027                 return rCandidate;
1028             }
1029         }
1030 
1031         ////////////////////////////////////////////////////////////////////////////////
1032 
addPointsAtCutsAndTouches(const B2DPolyPolygon & rMask,const B2DPolyPolygon & rCandidate)1033         B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolyPolygon& rCandidate)
1034         {
1035             B2DPolyPolygon aRetval;
1036 
1037             for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
1038             {
1039                 aRetval.append(addPointsAtCutsAndTouches(rMask, rCandidate.getB2DPolygon(a)));
1040             }
1041 
1042             return aRetval;
1043         }
1044 
1045         ////////////////////////////////////////////////////////////////////////////////
1046 
addPointsAtCuts(const B2DPolygon & rCandidate,const B2DPoint & rStart,const B2DPoint & rEnd)1047         B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd)
1048         {
1049             const sal_uInt32 nCount(rCandidate.count());
1050 
1051             if(nCount && !rStart.equal(rEnd))
1052             {
1053                 const B2DRange aPolygonRange(rCandidate.getB2DRange());
1054                 const B2DRange aEdgeRange(rStart, rEnd);
1055 
1056                 if(aPolygonRange.overlaps(aEdgeRange))
1057                 {
1058                     const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nCount : nCount - 1);
1059                     temporaryPointVector aTempPoints;
1060                     temporaryPointVector aUnusedTempPoints;
1061                     B2DCubicBezier aCubic;
1062 
1063                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
1064                     {
1065                         rCandidate.getBezierSegment(a, aCubic);
1066                         B2DRange aCubicRange(aCubic.getStartPoint(), aCubic.getEndPoint());
1067 
1068                         if(aCubic.isBezier())
1069                         {
1070                             aCubicRange.expand(aCubic.getControlPointA());
1071                             aCubicRange.expand(aCubic.getControlPointB());
1072 
1073                             if(aCubicRange.overlaps(aEdgeRange))
1074                             {
1075                                 findEdgeCutsBezierAndEdge(aCubic, rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints);
1076                             }
1077                         }
1078                         else
1079                         {
1080                             if(aCubicRange.overlaps(aEdgeRange))
1081                             {
1082                                 findEdgeCutsTwoEdges(aCubic.getStartPoint(), aCubic.getEndPoint(), rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints);
1083                             }
1084                         }
1085                     }
1086 
1087                     return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
1088                 }
1089             }
1090 
1091             return rCandidate;
1092         }
1093 
addPointsAtCuts(const B2DPolyPolygon & rCandidate,const B2DPoint & rStart,const B2DPoint & rEnd)1094         B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd)
1095         {
1096             B2DPolyPolygon aRetval;
1097 
1098             for(sal_uInt32 a(0); a < rCandidate.count(); a++)
1099             {
1100                 aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(a), rStart, rEnd));
1101             }
1102 
1103             return aRetval;
1104         }
1105 
1106         ////////////////////////////////////////////////////////////////////////////////
1107 
addPointsAtCuts(const B2DPolygon & rCandidate,const B2DPolyPolygon & rPolyMask)1108         B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPolyPolygon& rPolyMask)
1109         {
1110             const sal_uInt32 nCountA(rCandidate.count());
1111             const sal_uInt32 nCountM(rPolyMask.count());
1112 
1113             if(nCountA && nCountM)
1114             {
1115                 const B2DRange aRangeA(rCandidate.getB2DRange());
1116                 const B2DRange aRangeM(rPolyMask.getB2DRange());
1117 
1118                 if(aRangeA.overlaps(aRangeM))
1119                 {
1120                     const sal_uInt32 nEdgeCountA(rCandidate.isClosed() ? nCountA : nCountA - 1);
1121                     temporaryPointVector aTempPointsA;
1122                     temporaryPointVector aUnusedTempPointsB;
1123 
1124                     for(sal_uInt32 m(0); m < nCountM; m++)
1125                     {
1126                         const B2DPolygon aMask(rPolyMask.getB2DPolygon(m));
1127                         const sal_uInt32 nCountB(aMask.count());
1128 
1129                         if(nCountB)
1130                         {
1131                             B2DCubicBezier aCubicA;
1132                             B2DCubicBezier aCubicB;
1133 
1134                             for(sal_uInt32 a(0); a < nEdgeCountA; a++)
1135                             {
1136                                 rCandidate.getBezierSegment(a, aCubicA);
1137                                 const bool bCubicAIsCurve(aCubicA.isBezier());
1138                                 B2DRange aCubicRangeA(aCubicA.getStartPoint(), aCubicA.getEndPoint());
1139 
1140                                 if(bCubicAIsCurve)
1141                                 {
1142                                     aCubicRangeA.expand(aCubicA.getControlPointA());
1143                                     aCubicRangeA.expand(aCubicA.getControlPointB());
1144                                 }
1145 
1146                                 for(sal_uInt32 b(0); b < nCountB; b++)
1147                                 {
1148                                     aMask.getBezierSegment(b, aCubicB);
1149                                     const bool bCubicBIsCurve(aCubicB.isBezier());
1150                                     B2DRange aCubicRangeB(aCubicB.getStartPoint(), aCubicB.getEndPoint());
1151 
1152                                     if(bCubicBIsCurve)
1153                                     {
1154                                         aCubicRangeB.expand(aCubicB.getControlPointA());
1155                                         aCubicRangeB.expand(aCubicB.getControlPointB());
1156                                     }
1157 
1158                                     if(aCubicRangeA.overlaps(aCubicRangeB))
1159                                     {
1160                                         if(bCubicAIsCurve && bCubicBIsCurve)
1161                                         {
1162                                             findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, aTempPointsA, aUnusedTempPointsB);
1163                                         }
1164                                         else if(bCubicAIsCurve)
1165                                         {
1166                                             findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB);
1167                                         }
1168                                         else if(bCubicBIsCurve)
1169                                         {
1170                                             findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, aUnusedTempPointsB, aTempPointsA);
1171                                         }
1172                                         else
1173                                         {
1174                                             findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB);
1175                                         }
1176                                     }
1177                                 }
1178                             }
1179                         }
1180                     }
1181 
1182                     return mergeTemporaryPointsAndPolygon(rCandidate, aTempPointsA);
1183                 }
1184             }
1185 
1186             return rCandidate;
1187         }
1188 
addPointsAtCuts(const B2DPolyPolygon & rCandidate,const B2DPolyPolygon & rMask)1189         B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPolyPolygon& rMask)
1190         {
1191             B2DPolyPolygon aRetval;
1192 
1193             for(sal_uInt32 a(0); a < rCandidate.count(); a++)
1194             {
1195                 aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(a), rMask));
1196             }
1197 
1198             return aRetval;
1199         }
1200 
addPointsAtCuts(const B2DPolygon & rCandidate)1201         B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate)
1202         {
1203             if(rCandidate.count())
1204             {
1205                 temporaryPointVector aTempPoints;
1206 
1207                 findCuts(rCandidate, aTempPoints);
1208 
1209                 return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
1210             }
1211             else
1212             {
1213                 return rCandidate;
1214             }
1215         }
1216 
addPointsAtCuts(const B2DPolyPolygon & rCandidate,bool bSelfIntersections)1217         B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, bool bSelfIntersections)
1218         {
1219             const sal_uInt32 nCount(rCandidate.count());
1220 
1221             if(nCount)
1222             {
1223                 B2DPolyPolygon aRetval;
1224 
1225                 if(1 == nCount)
1226                 {
1227                     if(bSelfIntersections)
1228                     {
1229                         // remove self intersections
1230                         aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(0)));
1231                     }
1232                     else
1233                     {
1234                         // copy source
1235                         aRetval = rCandidate;
1236                     }
1237                 }
1238                 else
1239                 {
1240                     // first solve self cuts for all contained single polygons
1241                     temporaryPolygonData *pTempData = new temporaryPolygonData[nCount];
1242                     sal_uInt32 a, b;
1243 
1244                     for(a = 0; a < nCount; a++)
1245                     {
1246                         if(bSelfIntersections)
1247                         {
1248                             // use polygons with solved self intersections
1249                             pTempData[a].setPolygon(addPointsAtCuts(rCandidate.getB2DPolygon(a)));
1250                         }
1251                         else
1252                         {
1253                             // copy given polygons
1254                             pTempData[a].setPolygon(rCandidate.getB2DPolygon(a));
1255                         }
1256                     }
1257 
1258                     // now cuts and touches between the polygons
1259                     for(a = 0; a < nCount; a++)
1260                     {
1261                         for(b = 0; b < nCount; b++)
1262                         {
1263                             if(a < b)
1264                             {
1265                                 // look for cuts, compare each edge polygon to following ones
1266                                 if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
1267                                 {
1268                                     findCuts(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector(), pTempData[b].getTemporaryPointVector());
1269                                 }
1270                             }
1271                         }
1272                     }
1273 
1274                     // consolidate the result
1275                     for(a = 0L; a < nCount; a++)
1276                     {
1277                         aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector()));
1278                     }
1279 
1280                     delete[] pTempData;
1281                 }
1282 
1283                 return aRetval;
1284             }
1285             else
1286             {
1287                 return rCandidate;
1288             }
1289         }
1290 
1291         ////////////////////////////////////////////////////////////////////////////////
1292 
1293     } // end of namespace tools
1294 } // end of namespace basegfx
1295 
1296 //////////////////////////////////////////////////////////////////////////////
1297 // eof
1298