xref: /AOO41X/main/basegfx/source/polygon/b2dtrapezoid.cxx (revision ff86dd9e33cb614489a4af30c2a2ae101ee4b673)
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/b2dtrapezoid.hxx>
27 #include <basegfx/range/b1drange.hxx>
28 #include <basegfx/polygon/b2dpolygontools.hxx>
29 #include <list>
30 
31 //////////////////////////////////////////////////////////////////////////////
32 
33 namespace basegfx
34 {
35     namespace trapezoidhelper
36     {
37         //////////////////////////////////////////////////////////////////////////////
38         // helper class to hold a simple edge. This is only used for horizontal edges
39         // currently, thus the YPositions will be equal. I did not create a special
40         // class for this since holding the pointers is more effective and also can be
41         // used as baseclass for the traversing edges
42 
43         class TrDeSimpleEdge
44         {
45         protected:
46             // pointers to start and end point
47             const B2DPoint*     mpStart;
48             const B2DPoint*     mpEnd;
49 
50         public:
51             // constructor
TrDeSimpleEdge(const B2DPoint * pStart,const B2DPoint * pEnd)52             TrDeSimpleEdge(
53                 const B2DPoint* pStart,
54                 const B2DPoint* pEnd)
55             :   mpStart(pStart),
56                 mpEnd(pEnd)
57             {
58             }
59 
60             // data read access
getStart() const61             const B2DPoint& getStart() const { return *mpStart; }
getEnd() const62             const B2DPoint& getEnd() const { return *mpEnd; }
63         };
64 
65         //////////////////////////////////////////////////////////////////////////////
66         // define vector of simple edges
67 
68         typedef ::std::vector< TrDeSimpleEdge > TrDeSimpleEdges;
69 
70         //////////////////////////////////////////////////////////////////////////////
71         // helper class for holding a traversing edge. It will always have some
72         // distance in YPos. The slope (in a numerically useful form, see comments) is
73         // hold and used in SortValue to allow sorting traversing edges by Y, X and slope
74         // (in that order)
75 
76         class TrDeEdgeEntry : public TrDeSimpleEdge
77         {
78         private:
79             // the slope in a numerical useful form for sorting
80             sal_uInt32          mnSortValue;
81 
82         public:
83             // convenience data read access
getDeltaX() const84             double getDeltaX() const { return mpEnd->getX() - mpStart->getX(); }
getDeltaY() const85             double getDeltaY() const { return mpEnd->getY() - mpStart->getY(); }
86 
87             // convenience data read access. SortValue is created on demand since
88             // it is not always used
getSortValue() const89             sal_uInt32 getSortValue() const
90             {
91                 if(0 != mnSortValue)
92                     return mnSortValue;
93 
94                 // get radiant; has to be in the range ]0.0 .. pi[, thus scale to full
95                 // sal_uInt32 range for maximum precision
96                 const double fRadiant(atan2(getDeltaY(), getDeltaX()) * (SAL_MAX_UINT32 / F_PI));
97 
98                 // convert to sal_uInt32 value
99                 const_cast< TrDeEdgeEntry* >(this)->mnSortValue = sal_uInt32(fRadiant);
100 
101                 return mnSortValue;
102             }
103 
104             // constructor. SortValue can be given when known, use zero otherwise
TrDeEdgeEntry(const B2DPoint * pStart,const B2DPoint * pEnd,sal_uInt32 nSortValue=0)105             TrDeEdgeEntry(
106                 const B2DPoint* pStart,
107                 const B2DPoint* pEnd,
108                 sal_uInt32 nSortValue = 0)
109             :   TrDeSimpleEdge(pStart, pEnd),
110                 mnSortValue(nSortValue)
111             {
112                 // force traversal of deltaY downward
113                 if(mpEnd->getY() < mpStart->getY())
114                 {
115                     std::swap(mpStart, mpEnd);
116                 }
117 
118                 // no horizontal edges allowed, all need to traverse vertically
119                 OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
120             }
121 
122             // data write access to StartPoint
setStart(const B2DPoint * pNewStart)123             void setStart( const B2DPoint* pNewStart)
124             {
125                 OSL_ENSURE(0 != pNewStart, "No null pointer allowed here (!)");
126 
127                 if(mpStart != pNewStart)
128                 {
129                     mpStart = pNewStart;
130 
131                     // no horizontal edges allowed, all need to traverse vertically
132                     OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
133                 }
134             }
135 
136             // data write access to EndPoint
setEnd(const B2DPoint * pNewEnd)137             void setEnd( const B2DPoint* pNewEnd)
138             {
139                 OSL_ENSURE(0 != pNewEnd, "No null pointer allowed here (!)");
140 
141                 if(mpEnd != pNewEnd)
142                 {
143                     mpEnd = pNewEnd;
144 
145                     // no horizontal edges allowed, all need to traverse vertically
146                     OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
147                 }
148             }
149 
150             // operator for sort support. Sort by Y, X and slope (in that order)
operator <(const TrDeEdgeEntry & rComp) const151             bool operator<(const TrDeEdgeEntry& rComp) const
152             {
153                 if(fTools::equal(getStart().getY(), rComp.getStart().getY(), fTools::getSmallValue()))
154                 {
155                     if(fTools::equal(getStart().getX(), rComp.getStart().getX(), fTools::getSmallValue()))
156                     {
157                         // when start points are equal, use the direction the edge is pointing
158                         // to. That value is created on demand and derived from atan2 in the
159                         // range ]0.0 .. pi[ (without extremas, we always have a deltaY in this
160                         // class) and scaled to sal_uInt32 range for best precision. 0 means no angle,
161                         // while SAL_MAX_UINT32 means pi. Thus, the higher the value, the more left
162                         // the edge traverses.
163                         return (getSortValue() > rComp.getSortValue());
164                     }
165                     else
166                     {
167                         return fTools::less(getStart().getX(), rComp.getStart().getX());
168                     }
169                 }
170                 else
171                 {
172                     return fTools::less(getStart().getY(), rComp.getStart().getY());
173                 }
174             }
175 
176             // method for cut support
getCutPointForGivenY(double fGivenY)177             B2DPoint getCutPointForGivenY(double fGivenY)
178             {
179                 // Calculate cut point locally (do not use interpolate) since it is numerically
180                 // necessary to guarantee the new, equal Y-coordinate
181                 const double fFactor((fGivenY - getStart().getY()) / getDeltaY());
182                 const double fDeltaXNew(fFactor * getDeltaX());
183 
184                 return B2DPoint(getStart().getX() + fDeltaXNew, fGivenY);
185             }
186         };
187 
188         //////////////////////////////////////////////////////////////////////////////
189         // define double linked list of edges (for fast random insert)
190 
191         typedef ::std::list< TrDeEdgeEntry > TrDeEdgeEntries;
192 
193     } // end of anonymous namespace
194 } // end of namespace basegfx
195 
196 //////////////////////////////////////////////////////////////////////////////
197 
198 namespace basegfx
199 {
200     namespace trapezoidhelper
201     {
202         // helper class to handle the complete trapezoid subdivision of a PolyPolygon
203         class TrapezoidSubdivider
204         {
205         private:
206             // local data
207             sal_uInt32                  mnInitialEdgeEntryCount;
208             TrDeEdgeEntries             maTrDeEdgeEntries;
209             ::std::vector< B2DPoint >   maPoints;
210             ::std::vector< B2DPoint* >  maNewPoints;
211 
addEdgeSorted(TrDeEdgeEntries::iterator aCurrent,const TrDeEdgeEntry & rNewEdge)212             void addEdgeSorted(
213                 TrDeEdgeEntries::iterator aCurrent,
214                 const TrDeEdgeEntry& rNewEdge)
215             {
216                 // Loop while new entry is bigger, use operator<
217                 while(aCurrent != maTrDeEdgeEntries.end() && (*aCurrent) < rNewEdge)
218                 {
219                     aCurrent++;
220                 }
221 
222                 // Insert before first which is smaller or equal or at end
223                 maTrDeEdgeEntries.insert(aCurrent, rNewEdge);
224             }
225 
splitEdgeAtGivenPoint(TrDeEdgeEntries::reference aEdge,const B2DPoint & rCutPoint,TrDeEdgeEntries::iterator aCurrent)226             bool splitEdgeAtGivenPoint(
227                 TrDeEdgeEntries::reference aEdge,
228                 const B2DPoint& rCutPoint,
229                 TrDeEdgeEntries::iterator aCurrent)
230             {
231                 // do not create edges without deltaY: do not split when start is identical
232                 if(aEdge.getStart().equal(rCutPoint, fTools::getSmallValue()))
233                 {
234                     return false;
235                 }
236 
237                 // do not create edges without deltaY: do not split when end is identical
238                 if(aEdge.getEnd().equal(rCutPoint, fTools::getSmallValue()))
239                 {
240                     return false;
241                 }
242 
243                 const double fOldDeltaYStart(rCutPoint.getY() - aEdge.getStart().getY());
244 
245                 if(fTools::lessOrEqual(fOldDeltaYStart, 0.0))
246                 {
247                     // do not split: the resulting edge would be horizontal
248                     // correct it to new start point
249                     aEdge.setStart(&rCutPoint);
250                     return false;
251                 }
252 
253                 const double fNewDeltaYStart(aEdge.getEnd().getY() - rCutPoint.getY());
254 
255                 if(fTools::lessOrEqual(fNewDeltaYStart, 0.0))
256                 {
257                     // do not split: the resulting edge would be horizontal
258                     // correct it to new end point
259                     aEdge.setEnd(&rCutPoint);
260                     return false;
261                 }
262 
263                 // Create new entry
264                 const TrDeEdgeEntry aNewEdge(
265                     &rCutPoint,
266                     &aEdge.getEnd(),
267                     aEdge.getSortValue());
268 
269                 // Correct old entry
270                 aEdge.setEnd(&rCutPoint);
271 
272                 // Insert sorted (to avoid new sort)
273                 addEdgeSorted(aCurrent, aNewEdge);
274 
275                 return true;
276             }
277 
testAndCorrectEdgeIntersection(TrDeEdgeEntries::reference aEdgeA,TrDeEdgeEntries::reference aEdgeB,TrDeEdgeEntries::iterator aCurrent)278             bool testAndCorrectEdgeIntersection(
279                 TrDeEdgeEntries::reference aEdgeA,
280                 TrDeEdgeEntries::reference aEdgeB,
281                 TrDeEdgeEntries::iterator aCurrent)
282             {
283                 // Exclude simple cases: same start or end point
284                 if(aEdgeA.getStart().equal(aEdgeB.getStart(), fTools::getSmallValue()))
285                 {
286                     return false;
287                 }
288 
289                 if(aEdgeA.getStart().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
290                 {
291                     return false;
292                 }
293 
294                 if(aEdgeA.getEnd().equal(aEdgeB.getStart(), fTools::getSmallValue()))
295                 {
296                     return false;
297                 }
298 
299                 if(aEdgeA.getEnd().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
300                 {
301                     return false;
302                 }
303 
304                 // Exclude simple cases: one of the edges has no length anymore
305                 if(aEdgeA.getStart().equal(aEdgeA.getEnd(), fTools::getSmallValue()))
306                 {
307                     return false;
308                 }
309 
310                 if(aEdgeB.getStart().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
311                 {
312                     return false;
313                 }
314 
315                 // check if one point is on the other edge (a touch, not a cut)
316                 const B2DVector aDeltaB(aEdgeB.getDeltaX(), aEdgeB.getDeltaY());
317 
318                 if(tools::isPointOnEdge(aEdgeA.getStart(), aEdgeB.getStart(), aDeltaB))
319                 {
320                     return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getStart(), aCurrent);
321                 }
322 
323                 if(tools::isPointOnEdge(aEdgeA.getEnd(), aEdgeB.getStart(), aDeltaB))
324                 {
325                     return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getEnd(), aCurrent);
326                 }
327 
328                 const B2DVector aDeltaA(aEdgeA.getDeltaX(), aEdgeA.getDeltaY());
329 
330                 if(tools::isPointOnEdge(aEdgeB.getStart(), aEdgeA.getStart(), aDeltaA))
331                 {
332                     return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getStart(), aCurrent);
333                 }
334 
335                 if(tools::isPointOnEdge(aEdgeB.getEnd(), aEdgeA.getStart(), aDeltaA))
336                 {
337                     return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getEnd(), aCurrent);
338                 }
339 
340                 // check for cut inside edges. Use both t-values to choose the more precise
341                 // one later
342                 double fCutA(0.0);
343                 double fCutB(0.0);
344 
345                 if(tools::findCut(
346                     aEdgeA.getStart(), aDeltaA,
347                     aEdgeB.getStart(), aDeltaB,
348                     CUTFLAG_LINE,
349                     &fCutA,
350                     &fCutB))
351                 {
352                     // use a simple metric (length criteria) for choosing the numerically
353                     // better cut
354                     const double fSimpleLengthA(aDeltaA.getX() + aDeltaA.getY());
355                     const double fSimpleLengthB(aDeltaB.getX() + aDeltaB.getY());
356                     const bool bAIsLonger(fSimpleLengthA > fSimpleLengthB);
357                     B2DPoint* pNewPoint = bAIsLonger
358                         ? new B2DPoint(aEdgeA.getStart() + (fCutA * aDeltaA))
359                         : new B2DPoint(aEdgeB.getStart() + (fCutB * aDeltaB));
360                     bool bRetval(false);
361 
362                     // try to split both edges
363                     bRetval = splitEdgeAtGivenPoint(aEdgeA, *pNewPoint, aCurrent);
364                     bRetval |= splitEdgeAtGivenPoint(aEdgeB, *pNewPoint, aCurrent);
365 
366                     if(bRetval)
367                     {
368                         maNewPoints.push_back(pNewPoint);
369                     }
370                     else
371                     {
372                         delete pNewPoint;
373                     }
374 
375                     return bRetval;
376                 }
377 
378                 return false;
379             }
380 
solveHorizontalEdges(TrDeSimpleEdges & rTrDeSimpleEdges)381             void solveHorizontalEdges(TrDeSimpleEdges& rTrDeSimpleEdges)
382             {
383                 if(rTrDeSimpleEdges.size() && maTrDeEdgeEntries.size())
384                 {
385                     // there were horizontal edges. These can be excluded, but
386                     // cuts with other edges need to be solved and added before
387                     // ignoring them
388                     sal_uInt32 a(0);
389 
390                     for(a = 0; a < rTrDeSimpleEdges.size(); a++)
391                     {
392                         // get horizontal edge as candidate; prepare its range and fixed Y
393                         const TrDeSimpleEdge& rHorEdge = rTrDeSimpleEdges[a];
394                         const B1DRange aRange(rHorEdge.getStart().getX(), rHorEdge.getEnd().getX());
395                         const double fFixedY(rHorEdge.getStart().getY());
396 
397                         // loop over traversing edges
398                         TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
399 
400                         do
401                         {
402                             // get compare edge
403                             TrDeEdgeEntries::reference aCompare(*aCurrent++);
404 
405                             if(fTools::lessOrEqual(aCompare.getEnd().getY(), fFixedY))
406                             {
407                                 // edge ends above horizontal edge, continue
408                                 continue;
409                             }
410 
411                             if(fTools::moreOrEqual(aCompare.getStart().getY(), fFixedY))
412                             {
413                                 // edge starts below horizontal edge, continue
414                                 continue;
415                             }
416 
417                             // vertical overlap, get horizontal range
418                             const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
419 
420                             if(aRange.overlaps(aCompareRange))
421                             {
422                                 // possible cut, get cut point
423                                 const B2DPoint aSplit(aCompare.getCutPointForGivenY(fFixedY));
424 
425                                 if(fTools::more(aSplit.getX(), aRange.getMinimum())
426                                     && fTools::less(aSplit.getX(), aRange.getMaximum()))
427                                 {
428                                     // cut is in XRange of horizontal edge, potentially needed cut
429                                     B2DPoint* pNewPoint = new B2DPoint(aSplit);
430 
431                                     if(splitEdgeAtGivenPoint(aCompare, *pNewPoint, aCurrent))
432                                     {
433                                         maNewPoints.push_back(pNewPoint);
434                                     }
435                                     else
436                                     {
437                                         delete pNewPoint;
438                                     }
439                                 }
440                             }
441                         }
442                         while(aCurrent != maTrDeEdgeEntries.end()
443                             && fTools::less(aCurrent->getStart().getY(), fFixedY));
444                     }
445                 }
446             }
447 
448         public:
TrapezoidSubdivider(const B2DPolyPolygon & rSourcePolyPolygon)449             TrapezoidSubdivider(
450                 const B2DPolyPolygon& rSourcePolyPolygon)
451             :   mnInitialEdgeEntryCount(0),
452                 maTrDeEdgeEntries(),
453                 maPoints(),
454                 maNewPoints()
455             {
456                 B2DPolyPolygon aSource(rSourcePolyPolygon);
457                 const sal_uInt32 nPolygonCount(rSourcePolyPolygon.count());
458                 TrDeSimpleEdges aTrDeSimpleEdges;
459                 sal_uInt32 a(0), b(0);
460                 sal_uInt32 nAllPointCount(0);
461 
462                 // ensure there are no curves used
463                 if(aSource.areControlPointsUsed())
464                 {
465                     aSource = aSource.getDefaultAdaptiveSubdivision();
466                 }
467 
468                 for(a = 0; a < nPolygonCount; a++)
469                 {
470                     // 1st run: count points
471                     const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
472                     const sal_uInt32 nCount(aPolygonCandidate.count());
473 
474                     if(nCount > 2)
475                     {
476                         nAllPointCount += nCount;
477                     }
478                 }
479 
480                 if(nAllPointCount)
481                 {
482                     // reserve needed points. CAUTION: maPoints size is NOT to be changed anymore
483                     // after 2nd loop since pointers to it are used in the edges
484                     maPoints.reserve(nAllPointCount);
485 
486                     for(a = 0; a < nPolygonCount; a++)
487                     {
488                         // 2nd run: add points
489                         const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
490                         const sal_uInt32 nCount(aPolygonCandidate.count());
491 
492                         if(nCount > 2)
493                         {
494                             for(b = 0; b < nCount; b++)
495                             {
496                                 maPoints.push_back(aPolygonCandidate.getB2DPoint(b));
497                             }
498                         }
499                     }
500 
501                     // Moved the edge construction to a 3rd run: doing it in the 2nd run is
502                     // possible (and i used it), but requires a working vector::reserve()
503                     // implementation, else the vector will be reallocated and the pointers
504                     // in the edges may be wrong. Security first here.
505                     sal_uInt32 nStartIndex(0);
506 
507                     for(a = 0; a < nPolygonCount; a++)
508                     {
509                         const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
510                         const sal_uInt32 nCount(aPolygonCandidate.count());
511 
512                         if(nCount > 2)
513                         {
514                             // get the last point of the current polygon
515                             B2DPoint* pPrev(&maPoints[nCount + nStartIndex - 1]);
516 
517                             for(b = 0; b < nCount; b++)
518                             {
519                                 // get next point
520                                 B2DPoint* pCurr(&maPoints[nStartIndex++]);
521 
522                                 if(fTools::equal(pPrev->getY(), pCurr->getY(), fTools::getSmallValue()))
523                                 {
524                                     // horizontal edge, check for single point
525                                     if(!fTools::equal(pPrev->getX(), pCurr->getX(), fTools::getSmallValue()))
526                                     {
527                                         // X-order not needed, just add
528                                         aTrDeSimpleEdges.push_back(TrDeSimpleEdge(pPrev, pCurr));
529 
530                                         const double fMiddle((pPrev->getY() + pCurr->getY()) * 0.5);
531                                         pPrev->setY(fMiddle);
532                                         pCurr->setY(fMiddle);
533                                     }
534                                 }
535                                 else
536                                 {
537                                     // vertical edge. Positive Y-direction is guaranteed by the
538                                     // TrDeEdgeEntry constructor
539                                     maTrDeEdgeEntries.push_back(TrDeEdgeEntry(pPrev, pCurr, 0));
540                                     mnInitialEdgeEntryCount++;
541                                 }
542 
543                                 // prepare next step
544                                 pPrev = pCurr;
545                             }
546                         }
547                     }
548                 }
549 
550                 if(maTrDeEdgeEntries.size())
551                 {
552                     // single and initial sort of traversing edges
553                     maTrDeEdgeEntries.sort();
554 
555                     // solve horizontal edges if there are any detected
556                     solveHorizontalEdges(aTrDeSimpleEdges);
557                 }
558             }
559 
~TrapezoidSubdivider()560             ~TrapezoidSubdivider()
561             {
562                 // delete the extra points created for cuts
563                 const sal_uInt32 nCount(maNewPoints.size());
564 
565                 for(sal_uInt32 a(0); a < nCount; a++)
566                 {
567                     delete maNewPoints[a];
568                 }
569             }
570 
Subdivide(B2DTrapezoidVector & ro_Result)571             void Subdivide(B2DTrapezoidVector& ro_Result)
572             {
573                 // This is the central subdivider. The strategy is to use the first two entries
574                 // from the traversing edges as a potential trapezoid and do the needed corrections
575                 // and adaptions on the way.
576                 //
577                 // There always must be two edges with the same YStart value: When adding the polygons
578                 // in the constructor, there is always a topmost point from which two edges start; when
579                 // the topmost is an edge, there is a start and end of this edge from which two edges
580                 // start. All cases have two edges with same StartY (QED).
581                 //
582                 // Based on this these edges get corrected when:
583                 // - one is longer than the other
584                 // - they intersect
585                 // - they intersect with other edges
586                 // - another edge starts inside the thought trapezoid
587                 //
588                 // All this cases again produce a valid state so that the first two edges have a common
589                 // Ystart again. Some cases lead to a restart of the process, some allow consuming the
590                 // edges and create the intended trapezoid.
591                 //
592                 // Be careful when doing changes here: It is essential to keep all possible paths
593                 // in valid states and to be numerically correct. This is especially needed e.g.
594                 // by using fTools::equal(..) in the more robust small-value incarnation.
595                 B1DRange aLeftRange;
596                 B1DRange aRightRange;
597 
598                 if(!maTrDeEdgeEntries.empty())
599                 {
600                     // measuring shows that the relation between edges and created trapezoids is
601                     // mostly in the 1:1 range, thus reserve as much trapezoids as edges exist. Do
602                     // not use maTrDeEdgeEntries.size() since that may be a non-constant time
603                     // operation for Lists. Instead, use mnInitialEdgeEntryCount which will contain
604                     // the roughly counted adds to the List
605                     ro_Result.reserve(ro_Result.size() + mnInitialEdgeEntryCount);
606                 }
607 
608                 while(!maTrDeEdgeEntries.empty())
609                 {
610                     // Prepare current operator and get first edge
611                     TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
612                     TrDeEdgeEntries::reference aLeft(*aCurrent++);
613 
614                     if(aCurrent == maTrDeEdgeEntries.end())
615                     {
616                         // Should not happen: No 2nd edge; consume the single edge
617                         // to not have an endless loop and start next. During development
618                         // i constantly had breakpoints here, so i am sure enough to add an
619                         // assertion here
620                         OSL_ENSURE(false, "Trapeziod decomposer in illegal state (!)");
621                         maTrDeEdgeEntries.pop_front();
622                         continue;
623                     }
624 
625                     // get second edge
626                     TrDeEdgeEntries::reference aRight(*aCurrent++);
627 
628                     if(!fTools::equal(aLeft.getStart().getY(), aRight.getStart().getY(), fTools::getSmallValue()))
629                     {
630                         // Should not happen: We have a 2nd edge, but YStart is on another
631                         // line; consume the single edge to not have an endless loop and start
632                         // next. During development i constantly had breakpoints here, so i am
633                         // sure enough to add an assertion here
634                         OSL_ENSURE(false, "Trapeziod decomposer in illegal state (!)");
635                         maTrDeEdgeEntries.pop_front();
636                         continue;
637                     }
638 
639                     // aLeft and aRight build a thought trapezoid now. They have a common
640                     // start line (same Y for start points). Potentially, one of the edges
641                     // is longer than the other. It is only needed to look at the shorter
642                     // length which build the potential trapezoid. To do so, get the end points
643                     // locally and adapt the evtl. longer one. Use only aLeftEnd and aRightEnd
644                     // from here on, not the aLeft.getEnd() or aRight.getEnd() accesses.
645                     B2DPoint aLeftEnd(aLeft.getEnd());
646                     B2DPoint aRightEnd(aRight.getEnd());
647 
648                     // check if end points are on the same line. If yes, no adaption
649                     // needs to be prepared. Also remember which one actually is longer.
650                     const bool bEndOnSameLine(fTools::equal(aLeftEnd.getY(), aRightEnd.getY(), fTools::getSmallValue()));
651                     bool bLeftIsLonger(false);
652 
653                     if(!bEndOnSameLine)
654                     {
655                         // check which edge is longer and correct accordingly
656                         bLeftIsLonger = fTools::more(aLeftEnd.getY(), aRightEnd.getY());
657 
658                         if(bLeftIsLonger)
659                         {
660                             aLeftEnd = aLeft.getCutPointForGivenY(aRightEnd.getY());
661                         }
662                         else
663                         {
664                             aRightEnd = aRight.getCutPointForGivenY(aLeftEnd.getY());
665                         }
666                     }
667 
668                     // check for same start and end points
669                     const bool bSameStartPoint(aLeft.getStart().equal(aRight.getStart(), fTools::getSmallValue()));
670                     const bool bSameEndPoint(aLeftEnd.equal(aRightEnd, fTools::getSmallValue()));
671 
672                     // check the simple case that the edges form a 'blind' edge (deadend)
673                     if(bSameStartPoint && bSameEndPoint)
674                     {
675                         // correct the longer edge if prepared
676                         if(!bEndOnSameLine)
677                         {
678                             if(bLeftIsLonger)
679                             {
680                                 B2DPoint* pNewPoint = new B2DPoint(aLeftEnd);
681 
682                                 if(splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
683                                 {
684                                     maNewPoints.push_back(pNewPoint);
685                                 }
686                                 else
687                                 {
688                                     delete pNewPoint;
689                                 }
690                             }
691                             else
692                             {
693                                 B2DPoint* pNewPoint = new B2DPoint(aRightEnd);
694 
695                                 if(splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
696                                 {
697                                     maNewPoints.push_back(pNewPoint);
698                                 }
699                                 else
700                                 {
701                                     delete pNewPoint;
702                                 }
703                             }
704                         }
705 
706                         // consume both edges and start next run
707                         maTrDeEdgeEntries.pop_front();
708                         maTrDeEdgeEntries.pop_front();
709 
710                         continue;
711                     }
712 
713                     // check if the edges self-intersect. This can only happen when
714                     // start and end point are different
715                     bool bRangesSet(false);
716 
717                     if(!(bSameStartPoint || bSameEndPoint))
718                     {
719                         // get XRanges of edges
720                         aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
721                         aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
722                         bRangesSet = true;
723 
724                         // use fast range test first
725                         if(aLeftRange.overlaps(aRightRange))
726                         {
727                             // real cut test and correction. If correction was needed,
728                             // start new run
729                             if(testAndCorrectEdgeIntersection(aLeft, aRight, aCurrent))
730                             {
731                                 continue;
732                             }
733                         }
734                     }
735 
736                     // now we need to check if there are intersections with other edges
737                     // or if other edges start inside the candidate trapezoid
738                     if(aCurrent != maTrDeEdgeEntries.end()
739                         && fTools::less(aCurrent->getStart().getY(), aLeftEnd.getY()))
740                     {
741                         // get XRanges of edges
742                         if(!bRangesSet)
743                         {
744                             aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
745                             aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
746                         }
747 
748                         // build full XRange for fast check
749                         B1DRange aAllRange(aLeftRange);
750                         aAllRange.expand(aRightRange);
751 
752                         // prepare loop iterator; aCurrent needs to stay unchanged for
753                         // eventual sorted insertions of new EdgeNodes. Also prepare stop flag
754                         TrDeEdgeEntries::iterator aLoop(aCurrent);
755                         bool bDone(false);
756 
757                         do
758                         {
759                             // get compare edge and its XRange
760                             TrDeEdgeEntries::reference aCompare(*aLoop++);
761 
762                             // avoid edges using the same start point as one of
763                             // the edges. These can neither have their start point
764                             // in the thought trapezoid nor cut with one of the edges
765                             if(aCompare.getStart().equal(aRight.getStart(), fTools::getSmallValue()))
766                             {
767                                 continue;
768                             }
769 
770                             // get compare XRange
771                             const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
772 
773                             // use fast range test first
774                             if(aAllRange.overlaps(aCompareRange))
775                             {
776                                 // check for start point inside thought trapezoid
777                                 if(fTools::more(aCompare.getStart().getY(), aLeft.getStart().getY()))
778                                 {
779                                     // calculate the two possible split points at compare's Y
780                                     const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aCompare.getStart().getY()));
781                                     const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aCompare.getStart().getY()));
782 
783                                     // check for start point of aCompare being inside thought
784                                     // trapezoid
785                                     if(aCompare.getStart().getX() >= aSplitLeft.getX() &&
786                                         aCompare.getStart().getX() <= aSplitRight.getX())
787                                     {
788                                         // is inside, correct and restart loop
789                                         B2DPoint* pNewLeft = new B2DPoint(aSplitLeft);
790 
791                                         if(splitEdgeAtGivenPoint(aLeft, *pNewLeft, aCurrent))
792                                         {
793                                             maNewPoints.push_back(pNewLeft);
794                                             bDone = true;
795                                         }
796                                         else
797                                         {
798                                             delete pNewLeft;
799                                         }
800 
801                                         B2DPoint* pNewRight = new B2DPoint(aSplitRight);
802 
803                                         if(splitEdgeAtGivenPoint(aRight, *pNewRight, aCurrent))
804                                         {
805                                             maNewPoints.push_back(pNewRight);
806                                             bDone = true;
807                                         }
808                                         else
809                                         {
810                                             delete pNewRight;
811                                         }
812                                     }
813                                 }
814 
815                                 if(!bDone && aLeftRange.overlaps(aCompareRange))
816                                 {
817                                     // test for concrete cut of compare edge with left edge
818                                     bDone = testAndCorrectEdgeIntersection(aLeft, aCompare, aCurrent);
819                                 }
820 
821                                 if(!bDone && aRightRange.overlaps(aCompareRange))
822                                 {
823                                     // test for concrete cut of compare edge with Right edge
824                                     bDone = testAndCorrectEdgeIntersection(aRight, aCompare, aCurrent);
825                                 }
826                             }
827                         }
828                         while(!bDone
829                             && aLoop != maTrDeEdgeEntries.end()
830                             && fTools::less(aLoop->getStart().getY(), aLeftEnd.getY()));
831 
832                         if(bDone)
833                         {
834                             // something needed to be changed; start next loop
835                             continue;
836                         }
837                     }
838 
839                     // when we get here, the intended trapezoid can be used. It needs to
840                     // be corrected, eventually (if prepared); but this is no reason not to
841                     // use it in the same loop iteration
842                     if(!bEndOnSameLine)
843                     {
844                         if(bLeftIsLonger)
845                         {
846                             B2DPoint* pNewPoint = new B2DPoint(aLeftEnd);
847 
848                             if(splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
849                             {
850                                 maNewPoints.push_back(pNewPoint);
851                             }
852                             else
853                             {
854                                 delete pNewPoint;
855                             }
856                         }
857                         else
858                         {
859                             B2DPoint* pNewPoint = new B2DPoint(aRightEnd);
860 
861                             if(splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
862                             {
863                                 maNewPoints.push_back(pNewPoint);
864                             }
865                             else
866                             {
867                                 delete pNewPoint;
868                             }
869                         }
870                     }
871 
872                     // the two edges start at the same Y, they use the same DeltaY, they
873                     // do not cut themselves and not any other edge in range. Create a
874                     // B2DTrapezoid and consume both edges
875                     ro_Result.push_back(
876                         B2DTrapezoid(
877                             aLeft.getStart().getX(),
878                             aRight.getStart().getX(),
879                             aLeft.getStart().getY(),
880                             aLeftEnd.getX(),
881                             aRightEnd.getX(),
882                             aLeftEnd.getY()));
883 
884                     maTrDeEdgeEntries.pop_front();
885                     maTrDeEdgeEntries.pop_front();
886                 }
887             }
888         };
889     } // end of anonymous namespace
890 } // end of namespace basegfx
891 
892 //////////////////////////////////////////////////////////////////////////////
893 
894 namespace basegfx
895 {
B2DTrapezoid(const double & rfTopXLeft,const double & rfTopXRight,const double & rfTopY,const double & rfBottomXLeft,const double & rfBottomXRight,const double & rfBottomY)896     B2DTrapezoid::B2DTrapezoid(
897         const double& rfTopXLeft,
898         const double& rfTopXRight,
899         const double& rfTopY,
900         const double& rfBottomXLeft,
901         const double& rfBottomXRight,
902         const double& rfBottomY)
903     :   mfTopXLeft(rfTopXLeft),
904         mfTopXRight(rfTopXRight),
905         mfTopY(rfTopY),
906         mfBottomXLeft(rfBottomXLeft),
907         mfBottomXRight(rfBottomXRight),
908         mfBottomY(rfBottomY)
909     {
910         // guarantee mfTopXRight >= mfTopXLeft
911         if(mfTopXLeft > mfTopXRight)
912         {
913             std::swap(mfTopXLeft, mfTopXRight);
914         }
915 
916         // guarantee mfBottomXRight >= mfBottomXLeft
917         if(mfBottomXLeft > mfBottomXRight)
918         {
919             std::swap(mfBottomXLeft, mfBottomXRight);
920         }
921 
922         // guarantee mfBottomY >= mfTopY
923         if(mfTopY > mfBottomY)
924         {
925             std::swap(mfTopY, mfBottomY);
926             std::swap(mfTopXLeft, mfBottomXLeft);
927             std::swap(mfTopXRight, mfBottomXRight);
928         }
929     }
930 
getB2DPolygon() const931     B2DPolygon B2DTrapezoid::getB2DPolygon() const
932     {
933         B2DPolygon aRetval;
934 
935         aRetval.append(B2DPoint(getTopXLeft(), getTopY()));
936         aRetval.append(B2DPoint(getTopXRight(), getTopY()));
937         aRetval.append(B2DPoint(getBottomXRight(), getBottomY()));
938         aRetval.append(B2DPoint(getBottomXLeft(), getBottomY()));
939         aRetval.setClosed(true);
940 
941         return aRetval;
942     }
943 } // end of namespace basegfx
944 
945 //////////////////////////////////////////////////////////////////////////////
946 
947 namespace basegfx
948 {
949     namespace tools
950     {
951         // convert Source PolyPolygon to trapezoids
trapezoidSubdivide(B2DTrapezoidVector & ro_Result,const B2DPolyPolygon & rSourcePolyPolygon)952         void trapezoidSubdivide(B2DTrapezoidVector& ro_Result, const B2DPolyPolygon& rSourcePolyPolygon)
953         {
954             trapezoidhelper::TrapezoidSubdivider aTrapezoidSubdivider(rSourcePolyPolygon);
955 
956             aTrapezoidSubdivider.Subdivide(ro_Result);
957         }
958 
createLineTrapezoidFromEdge(B2DTrapezoidVector & ro_Result,const B2DPoint & rPointA,const B2DPoint & rPointB,double fLineWidth)959         void createLineTrapezoidFromEdge(
960             B2DTrapezoidVector& ro_Result,
961             const B2DPoint& rPointA,
962             const B2DPoint& rPointB,
963             double fLineWidth)
964         {
965             if(fTools::lessOrEqual(fLineWidth, 0.0))
966             {
967                 // no line width
968                 return;
969             }
970 
971             if(rPointA.equal(rPointB, fTools::getSmallValue()))
972             {
973                 // points are equal, no edge
974                 return;
975             }
976 
977             const double fHalfLineWidth(0.5 * fLineWidth);
978 
979             if(fTools::equal(rPointA.getX(), rPointB.getX(), fTools::getSmallValue()))
980             {
981                 // vertical line
982                 const double fLeftX(rPointA.getX() - fHalfLineWidth);
983                 const double fRightX(rPointA.getX() + fHalfLineWidth);
984 
985                 ro_Result.push_back(
986                     B2DTrapezoid(
987                         fLeftX,
988                         fRightX,
989                         std::min(rPointA.getY(), rPointB.getY()),
990                         fLeftX,
991                         fRightX,
992                         std::max(rPointA.getY(), rPointB.getY())));
993             }
994             else if(fTools::equal(rPointA.getY(), rPointB.getY(), fTools::getSmallValue()))
995             {
996                 // horizontal line
997                 const double fLeftX(std::min(rPointA.getX(), rPointB.getX()));
998                 const double fRightX(std::max(rPointA.getX(), rPointB.getX()));
999 
1000                 ro_Result.push_back(
1001                     B2DTrapezoid(
1002                         fLeftX,
1003                         fRightX,
1004                         rPointA.getY() - fHalfLineWidth,
1005                         fLeftX,
1006                         fRightX,
1007                         rPointA.getY() + fHalfLineWidth));
1008             }
1009             else
1010             {
1011                 // diagonal line
1012                 // create perpendicular vector
1013                 const B2DVector aDelta(rPointB - rPointA);
1014                 B2DVector aPerpendicular(-aDelta.getY(), aDelta.getX());
1015                 aPerpendicular.setLength(fHalfLineWidth);
1016 
1017                 // create StartLow, StartHigh, EndLow and EndHigh
1018                 const B2DPoint aStartLow(rPointA + aPerpendicular);
1019                 const B2DPoint aStartHigh(rPointA - aPerpendicular);
1020                 const B2DPoint aEndHigh(rPointB - aPerpendicular);
1021                 const B2DPoint aEndLow(rPointB + aPerpendicular);
1022 
1023                 // create EdgeEntries
1024                 basegfx::trapezoidhelper::TrDeEdgeEntries aTrDeEdgeEntries;
1025 
1026                 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartLow, &aStartHigh, 0));
1027                 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartHigh, &aEndHigh, 0));
1028                 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndHigh, &aEndLow, 0));
1029                 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndLow, &aStartLow, 0));
1030                 aTrDeEdgeEntries.sort();
1031 
1032                 // here we know we have exactly four edges, and they do not cut, touch or
1033                 // intersect. This makes processing much easier. Get the first two as start
1034                 // edges for the thought trapezoid
1035                 basegfx::trapezoidhelper::TrDeEdgeEntries::iterator aCurrent(aTrDeEdgeEntries.begin());
1036                 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft(*aCurrent++);
1037                 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight(*aCurrent++);
1038                 const bool bEndOnSameLine(fTools::equal(aLeft.getEnd().getY(), aRight.getEnd().getY(), fTools::getSmallValue()));
1039 
1040                 if(bEndOnSameLine)
1041                 {
1042                     // create two triangle trapezoids
1043                     ro_Result.push_back(
1044                         B2DTrapezoid(
1045                             aLeft.getStart().getX(),
1046                             aRight.getStart().getX(),
1047                             aLeft.getStart().getY(),
1048                             aLeft.getEnd().getX(),
1049                             aRight.getEnd().getX(),
1050                             aLeft.getEnd().getY()));
1051 
1052                     basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1053                     basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1054 
1055                     ro_Result.push_back(
1056                         B2DTrapezoid(
1057                             aLeft2.getStart().getX(),
1058                             aRight2.getStart().getX(),
1059                             aLeft2.getStart().getY(),
1060                             aLeft2.getEnd().getX(),
1061                             aRight2.getEnd().getX(),
1062                             aLeft2.getEnd().getY()));
1063                 }
1064                 else
1065                 {
1066                     // create three trapezoids. Check which edge is longer and
1067                     // correct accordingly
1068                     const bool bLeftIsLonger(fTools::more(aLeft.getEnd().getY(), aRight.getEnd().getY()));
1069 
1070                     if(bLeftIsLonger)
1071                     {
1072                         basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1073                         basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1074                         const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aRight.getEnd().getY()));
1075                         const B2DPoint aSplitRight(aRight2.getCutPointForGivenY(aLeft.getEnd().getY()));
1076 
1077                         ro_Result.push_back(
1078                             B2DTrapezoid(
1079                                 aLeft.getStart().getX(),
1080                                 aRight.getStart().getX(),
1081                                 aLeft.getStart().getY(),
1082                                 aSplitLeft.getX(),
1083                                 aRight.getEnd().getX(),
1084                                 aRight.getEnd().getY()));
1085 
1086                         ro_Result.push_back(
1087                             B2DTrapezoid(
1088                                 aSplitLeft.getX(),
1089                                 aRight.getEnd().getX(),
1090                                 aRight.getEnd().getY(),
1091                                 aLeft2.getStart().getX(),
1092                                 aSplitRight.getX(),
1093                                 aLeft2.getStart().getY()));
1094 
1095                         ro_Result.push_back(
1096                             B2DTrapezoid(
1097                                 aLeft2.getStart().getX(),
1098                                 aSplitRight.getX(),
1099                                 aLeft2.getStart().getY(),
1100                                 aLeft2.getEnd().getX(),
1101                                 aRight2.getEnd().getX(),
1102                                 aLeft2.getEnd().getY()));
1103                     }
1104                     else
1105                     {
1106                         basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1107                         basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1108                         const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aLeft.getEnd().getY()));
1109                         const B2DPoint aSplitLeft(aLeft2.getCutPointForGivenY(aRight.getEnd().getY()));
1110 
1111                         ro_Result.push_back(
1112                             B2DTrapezoid(
1113                                 aLeft.getStart().getX(),
1114                                 aRight.getStart().getX(),
1115                                 aLeft.getStart().getY(),
1116                                 aLeft.getEnd().getX(),
1117                                 aSplitRight.getX(),
1118                                 aLeft.getEnd().getY()));
1119 
1120                         ro_Result.push_back(
1121                             B2DTrapezoid(
1122                                 aLeft.getEnd().getX(),
1123                                 aSplitRight.getX(),
1124                                 aLeft.getEnd().getY(),
1125                                 aSplitLeft.getX(),
1126                                 aRight.getEnd().getX(),
1127                                 aRight2.getStart().getY()));
1128 
1129                         ro_Result.push_back(
1130                             B2DTrapezoid(
1131                                 aSplitLeft.getX(),
1132                                 aRight.getEnd().getX(),
1133                                 aRight2.getStart().getY(),
1134                                 aLeft2.getEnd().getX(),
1135                                 aRight2.getEnd().getX(),
1136                                 aLeft2.getEnd().getY()));
1137                     }
1138                 }
1139             }
1140         }
1141 
createLineTrapezoidFromB2DPolygon(B2DTrapezoidVector & ro_Result,const B2DPolygon & rPolygon,double fLineWidth)1142         void createLineTrapezoidFromB2DPolygon(
1143             B2DTrapezoidVector& ro_Result,
1144             const B2DPolygon& rPolygon,
1145             double fLineWidth)
1146         {
1147             if(fTools::lessOrEqual(fLineWidth, 0.0))
1148             {
1149                 return;
1150             }
1151 
1152             // ensure there are no curves used
1153             B2DPolygon aSource(rPolygon);
1154 
1155             if(aSource.areControlPointsUsed())
1156             {
1157             const double fPrecisionFactor = 0.25;
1158                 aSource = adaptiveSubdivideByDistance( aSource, fLineWidth * fPrecisionFactor );
1159             }
1160 
1161             const sal_uInt32 nPointCount(aSource.count());
1162 
1163             if(!nPointCount)
1164             {
1165                 return;
1166             }
1167 
1168             const sal_uInt32 nEdgeCount(aSource.isClosed() ? nPointCount : nPointCount - 1);
1169             B2DPoint aCurrent(aSource.getB2DPoint(0));
1170 
1171             ro_Result.reserve(ro_Result.size() + (3 * nEdgeCount));
1172 
1173             for(sal_uInt32 a(0); a < nEdgeCount; a++)
1174             {
1175                 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
1176                 const B2DPoint aNext(aSource.getB2DPoint(nNextIndex));
1177 
1178                 createLineTrapezoidFromEdge(ro_Result, aCurrent, aNext, fLineWidth);
1179                 aCurrent = aNext;
1180             }
1181         }
1182 
createLineTrapezoidFromB2DPolyPolygon(B2DTrapezoidVector & ro_Result,const B2DPolyPolygon & rPolyPolygon,double fLineWidth)1183         void createLineTrapezoidFromB2DPolyPolygon(
1184             B2DTrapezoidVector& ro_Result,
1185             const B2DPolyPolygon& rPolyPolygon,
1186             double fLineWidth)
1187         {
1188             if(fTools::lessOrEqual(fLineWidth, 0.0))
1189             {
1190                 return;
1191             }
1192 
1193             // ensure there are no curves used
1194             B2DPolyPolygon aSource(rPolyPolygon);
1195 
1196             if(aSource.areControlPointsUsed())
1197             {
1198                 aSource = aSource.getDefaultAdaptiveSubdivision();
1199             }
1200 
1201             const sal_uInt32 nCount(aSource.count());
1202 
1203             if(!nCount)
1204             {
1205                 return;
1206             }
1207 
1208             for(sal_uInt32 a(0); a < nCount; a++)
1209             {
1210                 createLineTrapezoidFromB2DPolygon(
1211                     ro_Result,
1212                     aSource.getB2DPolygon(a),
1213                     fLineWidth);
1214             }
1215         }
1216 
1217     } // end of namespace tools
1218 } // end of namespace basegfx
1219 
1220 //////////////////////////////////////////////////////////////////////////////
1221 // eof
1222