1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <osl/diagnose.h>
21 #include <basegfx/polygon/b2dpolygon.hxx>
22 #include <basegfx/point/b2dpoint.hxx>
23 #include <basegfx/vector/b2dvector.hxx>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <basegfx/curve/b2dcubicbezier.hxx>
26 #include <basegfx/polygon/b2dpolygontools.hxx>
27 #include <basegfx/utils/systemdependentdata.hxx>
28 #include <algorithm>
29 #include <memory>
30 #include <vector>
31 
32 struct CoordinateData2D : public basegfx::B2DPoint
33 {
34 public:
CoordinateData2DCoordinateData2D35     CoordinateData2D() {}
36 
CoordinateData2DCoordinateData2D37     explicit CoordinateData2D(const basegfx::B2DPoint& rData)
38     :   B2DPoint(rData)
39     {}
40 
operator =CoordinateData2D41     CoordinateData2D& operator=(const basegfx::B2DPoint& rData)
42     {
43         B2DPoint::operator=(rData);
44         return *this;
45     }
46 
transformCoordinateData2D47     void transform(const basegfx::B2DHomMatrix& rMatrix)
48     {
49         *this *= rMatrix;
50     }
51 };
52 
53 class CoordinateDataArray2D
54 {
55     typedef std::vector< CoordinateData2D > CoordinateData2DVector;
56 
57     CoordinateData2DVector                          maVector;
58 
59 public:
CoordinateDataArray2D(sal_uInt32 nCount)60     explicit CoordinateDataArray2D(sal_uInt32 nCount)
61     :   maVector(nCount)
62     {
63     }
64 
CoordinateDataArray2D(const CoordinateDataArray2D & rOriginal,sal_uInt32 nIndex,sal_uInt32 nCount)65     CoordinateDataArray2D(const CoordinateDataArray2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
66     :   maVector(rOriginal.maVector.begin() + nIndex, rOriginal.maVector.begin() + (nIndex + nCount))
67     {
68     }
69 
count() const70     sal_uInt32 count() const
71     {
72         return maVector.size();
73     }
74 
operator ==(const CoordinateDataArray2D & rCandidate) const75     bool operator==(const CoordinateDataArray2D& rCandidate) const
76     {
77         return (maVector == rCandidate.maVector);
78     }
79 
getCoordinate(sal_uInt32 nIndex) const80     const basegfx::B2DPoint& getCoordinate(sal_uInt32 nIndex) const
81     {
82         return maVector[nIndex];
83     }
84 
setCoordinate(sal_uInt32 nIndex,const basegfx::B2DPoint & rValue)85     void setCoordinate(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue)
86     {
87         maVector[nIndex] = rValue;
88     }
89 
reserve(sal_uInt32 nCount)90     void reserve(sal_uInt32 nCount)
91     {
92         maVector.reserve(nCount);
93     }
94 
append(const CoordinateData2D & rValue)95     void append(const CoordinateData2D& rValue)
96     {
97         maVector.push_back(rValue);
98     }
99 
insert(sal_uInt32 nIndex,const CoordinateData2D & rValue,sal_uInt32 nCount)100     void insert(sal_uInt32 nIndex, const CoordinateData2D& rValue, sal_uInt32 nCount)
101     {
102         if(nCount)
103         {
104             // add nCount copies of rValue
105             CoordinateData2DVector::iterator aIndex(maVector.begin());
106             aIndex += nIndex;
107             maVector.insert(aIndex, nCount, rValue);
108         }
109     }
110 
insert(sal_uInt32 nIndex,const CoordinateDataArray2D & rSource)111     void insert(sal_uInt32 nIndex, const CoordinateDataArray2D& rSource)
112     {
113         const sal_uInt32 nCount(rSource.maVector.size());
114 
115         if(nCount)
116         {
117             // insert data
118             CoordinateData2DVector::iterator aIndex(maVector.begin());
119             aIndex += nIndex;
120             CoordinateData2DVector::const_iterator aStart(rSource.maVector.begin());
121             CoordinateData2DVector::const_iterator aEnd(rSource.maVector.end());
122             maVector.insert(aIndex, aStart, aEnd);
123         }
124     }
125 
remove(sal_uInt32 nIndex,sal_uInt32 nCount)126     void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
127     {
128         if(nCount)
129         {
130             // remove point data
131             CoordinateData2DVector::iterator aStart(maVector.begin());
132             aStart += nIndex;
133             const CoordinateData2DVector::iterator aEnd(aStart + nCount);
134             maVector.erase(aStart, aEnd);
135         }
136     }
137 
flip(bool bIsClosed)138     void flip(bool bIsClosed)
139     {
140         if(maVector.size() > 1)
141         {
142             // to keep the same point at index 0, just flip all points except the
143             // first one when closed
144             const sal_uInt32 nHalfSize(bIsClosed ? (maVector.size() - 1) >> 1 : maVector.size() >> 1);
145             CoordinateData2DVector::iterator aStart(bIsClosed ? maVector.begin() + 1 : maVector.begin());
146             CoordinateData2DVector::iterator aEnd(maVector.end() - 1);
147 
148             for(sal_uInt32 a(0); a < nHalfSize; a++)
149             {
150                 std::swap(*aStart, *aEnd);
151                 ++aStart;
152                 --aEnd;
153             }
154         }
155     }
156 
removeDoublePointsAtBeginEnd()157     void removeDoublePointsAtBeginEnd()
158     {
159         // remove from end as long as there are at least two points
160         // and begin/end are equal
161         while((maVector.size() > 1) && (maVector[0] == maVector[maVector.size() - 1]))
162         {
163             maVector.pop_back();
164         }
165     }
166 
removeDoublePointsWholeTrack()167     void removeDoublePointsWholeTrack()
168     {
169         sal_uInt32 nIndex(0);
170 
171         // test as long as there are at least two points and as long as the index
172         // is smaller or equal second last point
173         while((maVector.size() > 1) && (nIndex <= maVector.size() - 2))
174         {
175             if(maVector[nIndex] == maVector[nIndex + 1])
176             {
177                 // if next is same as index, delete next
178                 maVector.erase(maVector.begin() + (nIndex + 1));
179             }
180             else
181             {
182                 // if different, step forward
183                 nIndex++;
184             }
185         }
186     }
187 
transform(const basegfx::B2DHomMatrix & rMatrix)188     void transform(const basegfx::B2DHomMatrix& rMatrix)
189     {
190         for (auto & elem : maVector)
191         {
192             elem.transform(rMatrix);
193         }
194     }
195 };
196 
197 class ControlVectorPair2D
198 {
199     basegfx::B2DVector                          maPrevVector;
200     basegfx::B2DVector                          maNextVector;
201 
202 public:
ControlVectorPair2D()203     explicit ControlVectorPair2D() {}
204 
getPrevVector() const205     const basegfx::B2DVector& getPrevVector() const
206     {
207         return maPrevVector;
208     }
209 
setPrevVector(const basegfx::B2DVector & rValue)210     void setPrevVector(const basegfx::B2DVector& rValue)
211     {
212         if(rValue != maPrevVector)
213             maPrevVector = rValue;
214     }
215 
getNextVector() const216     const basegfx::B2DVector& getNextVector() const
217     {
218         return maNextVector;
219     }
220 
setNextVector(const basegfx::B2DVector & rValue)221     void setNextVector(const basegfx::B2DVector& rValue)
222     {
223         if(rValue != maNextVector)
224             maNextVector = rValue;
225     }
226 
operator ==(const ControlVectorPair2D & rData) const227     bool operator==(const ControlVectorPair2D& rData) const
228     {
229         return (maPrevVector == rData.getPrevVector() && maNextVector == rData.getNextVector());
230     }
231 
flip()232     void flip()
233     {
234         std::swap(maPrevVector, maNextVector);
235     }
236 };
237 
238 class ControlVectorArray2D
239 {
240     typedef std::vector< ControlVectorPair2D > ControlVectorPair2DVector;
241 
242     ControlVectorPair2DVector                           maVector;
243     sal_uInt32                                          mnUsedVectors;
244 
245 public:
ControlVectorArray2D(sal_uInt32 nCount)246     explicit ControlVectorArray2D(sal_uInt32 nCount)
247     :   maVector(nCount),
248         mnUsedVectors(0)
249     {}
250 
ControlVectorArray2D(const ControlVectorArray2D & rOriginal,sal_uInt32 nIndex,sal_uInt32 nCount)251     ControlVectorArray2D(const ControlVectorArray2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
252     :   maVector(),
253         mnUsedVectors(0)
254     {
255         ControlVectorPair2DVector::const_iterator aStart(rOriginal.maVector.begin());
256         aStart += nIndex;
257         ControlVectorPair2DVector::const_iterator aEnd(aStart);
258         aEnd += nCount;
259         maVector.reserve(nCount);
260 
261         for(; aStart != aEnd; ++aStart)
262         {
263             if(!aStart->getPrevVector().equalZero())
264                 mnUsedVectors++;
265 
266             if(!aStart->getNextVector().equalZero())
267                 mnUsedVectors++;
268 
269             maVector.push_back(*aStart);
270         }
271     }
272 
operator ==(const ControlVectorArray2D & rCandidate) const273     bool operator==(const ControlVectorArray2D& rCandidate) const
274     {
275         return (maVector == rCandidate.maVector);
276     }
277 
isUsed() const278     bool isUsed() const
279     {
280         return (mnUsedVectors != 0);
281     }
282 
getPrevVector(sal_uInt32 nIndex) const283     const basegfx::B2DVector& getPrevVector(sal_uInt32 nIndex) const
284     {
285         return maVector[nIndex].getPrevVector();
286     }
287 
setPrevVector(sal_uInt32 nIndex,const basegfx::B2DVector & rValue)288     void setPrevVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
289     {
290         bool bWasUsed(mnUsedVectors && !maVector[nIndex].getPrevVector().equalZero());
291         bool bIsUsed(!rValue.equalZero());
292 
293         if(bWasUsed)
294         {
295             if(bIsUsed)
296             {
297                 maVector[nIndex].setPrevVector(rValue);
298             }
299             else
300             {
301                 maVector[nIndex].setPrevVector(basegfx::B2DVector::getEmptyVector());
302                 mnUsedVectors--;
303             }
304         }
305         else
306         {
307             if(bIsUsed)
308             {
309                 maVector[nIndex].setPrevVector(rValue);
310                 mnUsedVectors++;
311             }
312         }
313     }
314 
getNextVector(sal_uInt32 nIndex) const315     const basegfx::B2DVector& getNextVector(sal_uInt32 nIndex) const
316     {
317         return maVector[nIndex].getNextVector();
318     }
319 
setNextVector(sal_uInt32 nIndex,const basegfx::B2DVector & rValue)320     void setNextVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
321     {
322         bool bWasUsed(mnUsedVectors && !maVector[nIndex].getNextVector().equalZero());
323         bool bIsUsed(!rValue.equalZero());
324 
325         if(bWasUsed)
326         {
327             if(bIsUsed)
328             {
329                 maVector[nIndex].setNextVector(rValue);
330             }
331             else
332             {
333                 maVector[nIndex].setNextVector(basegfx::B2DVector::getEmptyVector());
334                 mnUsedVectors--;
335             }
336         }
337         else
338         {
339             if(bIsUsed)
340             {
341                 maVector[nIndex].setNextVector(rValue);
342                 mnUsedVectors++;
343             }
344         }
345     }
346 
append(const ControlVectorPair2D & rValue)347     void append(const ControlVectorPair2D& rValue)
348     {
349         maVector.push_back(rValue);
350 
351         if(!rValue.getPrevVector().equalZero())
352             mnUsedVectors += 1;
353 
354         if(!rValue.getNextVector().equalZero())
355             mnUsedVectors += 1;
356     }
357 
insert(sal_uInt32 nIndex,const ControlVectorPair2D & rValue,sal_uInt32 nCount)358     void insert(sal_uInt32 nIndex, const ControlVectorPair2D& rValue, sal_uInt32 nCount)
359     {
360         if(nCount)
361         {
362             // add nCount copies of rValue
363             ControlVectorPair2DVector::iterator aIndex(maVector.begin());
364             aIndex += nIndex;
365             maVector.insert(aIndex, nCount, rValue);
366 
367             if(!rValue.getPrevVector().equalZero())
368                 mnUsedVectors += nCount;
369 
370             if(!rValue.getNextVector().equalZero())
371                 mnUsedVectors += nCount;
372         }
373     }
374 
insert(sal_uInt32 nIndex,const ControlVectorArray2D & rSource)375     void insert(sal_uInt32 nIndex, const ControlVectorArray2D& rSource)
376     {
377         const sal_uInt32 nCount(rSource.maVector.size());
378 
379         if(nCount)
380         {
381             // insert data
382             ControlVectorPair2DVector::iterator aIndex(maVector.begin());
383             aIndex += nIndex;
384             ControlVectorPair2DVector::const_iterator aStart(rSource.maVector.begin());
385             ControlVectorPair2DVector::const_iterator aEnd(rSource.maVector.end());
386             maVector.insert(aIndex, aStart, aEnd);
387 
388             for(; aStart != aEnd; ++aStart)
389             {
390                 if(!aStart->getPrevVector().equalZero())
391                     mnUsedVectors++;
392 
393                 if(!aStart->getNextVector().equalZero())
394                     mnUsedVectors++;
395             }
396         }
397     }
398 
remove(sal_uInt32 nIndex,sal_uInt32 nCount)399     void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
400     {
401         if(nCount)
402         {
403             const ControlVectorPair2DVector::iterator aDeleteStart(maVector.begin() + nIndex);
404             const ControlVectorPair2DVector::iterator aDeleteEnd(aDeleteStart + nCount);
405             ControlVectorPair2DVector::const_iterator aStart(aDeleteStart);
406 
407             for(; mnUsedVectors && aStart != aDeleteEnd; ++aStart)
408             {
409                 if(!aStart->getPrevVector().equalZero())
410                     mnUsedVectors--;
411 
412                 if(mnUsedVectors && !aStart->getNextVector().equalZero())
413                     mnUsedVectors--;
414             }
415 
416             // remove point data
417             maVector.erase(aDeleteStart, aDeleteEnd);
418         }
419     }
420 
flip(bool bIsClosed)421     void flip(bool bIsClosed)
422     {
423         if(maVector.size() > 1)
424         {
425             // to keep the same point at index 0, just flip all points except the
426             // first one when closed
427             const sal_uInt32 nHalfSize(bIsClosed ? (maVector.size() - 1) >> 1 : maVector.size() >> 1);
428             ControlVectorPair2DVector::iterator aStart(bIsClosed ? maVector.begin() + 1 : maVector.begin());
429             ControlVectorPair2DVector::iterator aEnd(maVector.end() - 1);
430 
431             for(sal_uInt32 a(0); a < nHalfSize; a++)
432             {
433                 // swap Prev and Next
434                 aStart->flip();
435                 aEnd->flip();
436 
437                 // swap entries
438                 std::swap(*aStart, *aEnd);
439 
440                 ++aStart;
441                 --aEnd;
442             }
443 
444             if(aStart == aEnd)
445             {
446                 // swap Prev and Next at middle element (if exists)
447                 aStart->flip();
448             }
449 
450             if(bIsClosed)
451             {
452                 // swap Prev and Next at start element
453                 maVector.begin()->flip();
454             }
455         }
456     }
457 };
458 
459 class ImplBufferedData : public basegfx::SystemDependentDataHolder
460 {
461 private:
462     // Possibility to hold the last subdivision
463     std::unique_ptr< basegfx::B2DPolygon >  mpDefaultSubdivision;
464 
465     // Possibility to hold the last B2DRange calculation
466     std::unique_ptr< basegfx::B2DRange >    mpB2DRange;
467 
468 public:
ImplBufferedData()469     ImplBufferedData()
470     :   basegfx::SystemDependentDataHolder(),
471         mpDefaultSubdivision(),
472         mpB2DRange()
473     {
474     }
475 
getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon & rSource) const476     const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
477     {
478         if(!mpDefaultSubdivision)
479         {
480             const_cast< ImplBufferedData* >(this)->mpDefaultSubdivision.reset(new basegfx::B2DPolygon(basegfx::utils::adaptiveSubdivideByAngle(rSource)));
481         }
482 
483         return *mpDefaultSubdivision;
484     }
485 
getB2DRange(const basegfx::B2DPolygon & rSource) const486     const basegfx::B2DRange& getB2DRange(const basegfx::B2DPolygon& rSource) const
487     {
488         if(!mpB2DRange)
489         {
490             basegfx::B2DRange aNewRange;
491             const sal_uInt32 nPointCount(rSource.count());
492 
493             if(nPointCount)
494             {
495                 for(sal_uInt32 a(0); a < nPointCount; a++)
496                 {
497                     aNewRange.expand(rSource.getB2DPoint(a));
498                 }
499 
500                 if(rSource.areControlPointsUsed())
501                 {
502                     const sal_uInt32 nEdgeCount(rSource.isClosed() ? nPointCount : nPointCount - 1);
503 
504                     if(nEdgeCount)
505                     {
506                         basegfx::B2DCubicBezier aEdge;
507                         aEdge.setStartPoint(rSource.getB2DPoint(0));
508 
509                         for(sal_uInt32 b(0); b < nEdgeCount; b++)
510                         {
511                             const sal_uInt32 nNextIndex((b + 1) % nPointCount);
512                             aEdge.setControlPointA(rSource.getNextControlPoint(b));
513                             aEdge.setControlPointB(rSource.getPrevControlPoint(nNextIndex));
514                             aEdge.setEndPoint(rSource.getB2DPoint(nNextIndex));
515 
516                             if(aEdge.isBezier())
517                             {
518                                 const basegfx::B2DRange aBezierRangeWithControlPoints(aEdge.getRange());
519 
520                                 if(!aNewRange.isInside(aBezierRangeWithControlPoints))
521                                 {
522                                     // the range with control points of the current edge is not completely
523                                     // inside the current range without control points. Expand current range by
524                                     // subdividing the bezier segment.
525                                     // Ideal here is a subdivision at the extreme values, so use
526                                     // getAllExtremumPositions to get all extremas in one run
527                                     std::vector< double > aExtremas;
528 
529                                     aExtremas.reserve(4);
530                                     aEdge.getAllExtremumPositions(aExtremas);
531 
532                                     const sal_uInt32 nExtremaCount(aExtremas.size());
533 
534                                     for(sal_uInt32 c(0); c < nExtremaCount; c++)
535                                     {
536                                         aNewRange.expand(aEdge.interpolatePoint(aExtremas[c]));
537                                     }
538                                 }
539                             }
540 
541                             // prepare next edge
542                             aEdge.setStartPoint(aEdge.getEndPoint());
543                         }
544                     }
545                 }
546             }
547 
548             const_cast< ImplBufferedData* >(this)->mpB2DRange.reset(new basegfx::B2DRange(aNewRange));
549         }
550 
551         return *mpB2DRange;
552     }
553 };
554 
555 class ImplB2DPolygon
556 {
557 private:
558     // The point vector. This vector exists always and defines the
559     // count of members.
560     CoordinateDataArray2D                           maPoints;
561 
562     // The control point vectors. This vectors are created on demand
563     // and may be zero.
564     std::unique_ptr< ControlVectorArray2D >       mpControlVector;
565 
566     // buffered data for e.g. default subdivision and range
567     std::unique_ptr< ImplBufferedData >           mpBufferedData;
568 
569     // flag which decides if this polygon is opened or closed
570     bool                                            mbIsClosed;
571 
572 public:
getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon & rSource) const573     const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
574     {
575         if(!mpControlVector || !mpControlVector->isUsed())
576         {
577             return rSource;
578         }
579 
580         if(!mpBufferedData)
581         {
582             const_cast< ImplB2DPolygon* >(this)->mpBufferedData.reset(new ImplBufferedData);
583         }
584 
585         return mpBufferedData->getDefaultAdaptiveSubdivision(rSource);
586     }
587 
getB2DRange(const basegfx::B2DPolygon & rSource) const588     const basegfx::B2DRange& getB2DRange(const basegfx::B2DPolygon& rSource) const
589     {
590         if(!mpBufferedData)
591         {
592             const_cast< ImplB2DPolygon* >(this)->mpBufferedData.reset(new ImplBufferedData);
593         }
594 
595         return mpBufferedData->getB2DRange(rSource);
596     }
597 
ImplB2DPolygon()598     ImplB2DPolygon()
599     :   maPoints(0),
600         mpControlVector(),
601         mpBufferedData(),
602         mbIsClosed(false)
603     {}
604 
ImplB2DPolygon(const ImplB2DPolygon & rToBeCopied)605     ImplB2DPolygon(const ImplB2DPolygon& rToBeCopied)
606     :   maPoints(rToBeCopied.maPoints),
607         mpControlVector(),
608         mpBufferedData(),
609         mbIsClosed(rToBeCopied.mbIsClosed)
610     {
611         // complete initialization using copy
612         if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
613         {
614             mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector) );
615         }
616     }
617 
ImplB2DPolygon(const ImplB2DPolygon & rToBeCopied,sal_uInt32 nIndex,sal_uInt32 nCount)618     ImplB2DPolygon(const ImplB2DPolygon& rToBeCopied, sal_uInt32 nIndex, sal_uInt32 nCount)
619     :   maPoints(rToBeCopied.maPoints, nIndex, nCount),
620         mpControlVector(),
621         mpBufferedData(),
622         mbIsClosed(rToBeCopied.mbIsClosed)
623     {
624         // complete initialization using partly copy
625         if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
626         {
627             mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector, nIndex, nCount) );
628 
629             if(!mpControlVector->isUsed())
630                 mpControlVector.reset();
631         }
632     }
633 
operator =(const ImplB2DPolygon & rOther)634     ImplB2DPolygon& operator=(const ImplB2DPolygon& rOther)
635     {
636         if (this != &rOther)
637         {
638             mpControlVector.reset();
639             mpBufferedData.reset();
640             maPoints = rOther.maPoints;
641             mbIsClosed = rOther.mbIsClosed;
642             if (rOther.mpControlVector && rOther.mpControlVector->isUsed())
643             {
644                 mpControlVector.reset( new ControlVectorArray2D(*rOther.mpControlVector) );
645 
646                 if(!mpControlVector->isUsed())
647                     mpControlVector.reset();
648             }
649         }
650         return *this;
651     }
652 
count() const653     sal_uInt32 count() const
654     {
655         return maPoints.count();
656     }
657 
isClosed() const658     bool isClosed() const
659     {
660         return mbIsClosed;
661     }
662 
setClosed(bool bNew)663     void setClosed(bool bNew)
664     {
665         if(bNew != mbIsClosed)
666         {
667             mpBufferedData.reset();
668             mbIsClosed = bNew;
669         }
670     }
671 
operator ==(const ImplB2DPolygon & rCandidate) const672     bool operator==(const ImplB2DPolygon& rCandidate) const
673     {
674         if(mbIsClosed == rCandidate.mbIsClosed)
675         {
676             if(maPoints == rCandidate.maPoints)
677             {
678                 bool bControlVectorsAreEqual(true);
679 
680                 if(mpControlVector)
681                 {
682                     if(rCandidate.mpControlVector)
683                     {
684                         bControlVectorsAreEqual = ((*mpControlVector) == (*rCandidate.mpControlVector));
685                     }
686                     else
687                     {
688                         // candidate has no control vector, so it's assumed all unused.
689                         bControlVectorsAreEqual = !mpControlVector->isUsed();
690                     }
691                 }
692                 else
693                 {
694                     if(rCandidate.mpControlVector)
695                     {
696                         // we have no control vector, so it's assumed all unused.
697                         bControlVectorsAreEqual = !rCandidate.mpControlVector->isUsed();
698                     }
699                 }
700 
701                 if(bControlVectorsAreEqual)
702                 {
703                     return true;
704                 }
705             }
706         }
707 
708         return false;
709     }
710 
getPoint(sal_uInt32 nIndex) const711     const basegfx::B2DPoint& getPoint(sal_uInt32 nIndex) const
712     {
713         return maPoints.getCoordinate(nIndex);
714     }
715 
setPoint(sal_uInt32 nIndex,const basegfx::B2DPoint & rValue)716     void setPoint(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue)
717     {
718         mpBufferedData.reset();
719         maPoints.setCoordinate(nIndex, rValue);
720     }
721 
reserve(sal_uInt32 nCount)722     void reserve(sal_uInt32 nCount)
723     {
724         maPoints.reserve(nCount);
725     }
726 
append(const basegfx::B2DPoint & rPoint)727     void append(const basegfx::B2DPoint& rPoint)
728     {
729         mpBufferedData.reset(); // TODO: is this needed?
730         const CoordinateData2D aCoordinate(rPoint);
731         maPoints.append(aCoordinate);
732 
733         if(mpControlVector)
734         {
735             const ControlVectorPair2D aVectorPair;
736             mpControlVector->append(aVectorPair);
737         }
738     }
739 
insert(sal_uInt32 nIndex,const basegfx::B2DPoint & rPoint,sal_uInt32 nCount)740     void insert(sal_uInt32 nIndex, const basegfx::B2DPoint& rPoint, sal_uInt32 nCount)
741     {
742         if(nCount)
743         {
744             mpBufferedData.reset();
745             CoordinateData2D aCoordinate(rPoint);
746             maPoints.insert(nIndex, aCoordinate, nCount);
747 
748             if(mpControlVector)
749             {
750                 ControlVectorPair2D aVectorPair;
751                 mpControlVector->insert(nIndex, aVectorPair, nCount);
752             }
753         }
754     }
755 
getPrevControlVector(sal_uInt32 nIndex) const756     const basegfx::B2DVector& getPrevControlVector(sal_uInt32 nIndex) const
757     {
758         if(mpControlVector)
759         {
760             return mpControlVector->getPrevVector(nIndex);
761         }
762         else
763         {
764             return basegfx::B2DVector::getEmptyVector();
765         }
766     }
767 
setPrevControlVector(sal_uInt32 nIndex,const basegfx::B2DVector & rValue)768     void setPrevControlVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
769     {
770         if(!mpControlVector)
771         {
772             if(!rValue.equalZero())
773             {
774                 mpBufferedData.reset();
775                 mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
776                 mpControlVector->setPrevVector(nIndex, rValue);
777             }
778         }
779         else
780         {
781             mpBufferedData.reset();
782             mpControlVector->setPrevVector(nIndex, rValue);
783 
784             if(!mpControlVector->isUsed())
785                 mpControlVector.reset();
786         }
787     }
788 
getNextControlVector(sal_uInt32 nIndex) const789     const basegfx::B2DVector& getNextControlVector(sal_uInt32 nIndex) const
790     {
791         if(mpControlVector)
792         {
793             return mpControlVector->getNextVector(nIndex);
794         }
795         else
796         {
797             return basegfx::B2DVector::getEmptyVector();
798         }
799     }
800 
setNextControlVector(sal_uInt32 nIndex,const basegfx::B2DVector & rValue)801     void setNextControlVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
802     {
803         if(!mpControlVector)
804         {
805             if(!rValue.equalZero())
806             {
807                 mpBufferedData.reset();
808                 mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
809                 mpControlVector->setNextVector(nIndex, rValue);
810             }
811         }
812         else
813         {
814             mpBufferedData.reset();
815             mpControlVector->setNextVector(nIndex, rValue);
816 
817             if(!mpControlVector->isUsed())
818                 mpControlVector.reset();
819         }
820     }
821 
areControlPointsUsed() const822     bool areControlPointsUsed() const
823     {
824         return (mpControlVector && mpControlVector->isUsed());
825     }
826 
resetControlVectors()827     void resetControlVectors()
828     {
829         mpBufferedData.reset();
830         mpControlVector.reset();
831     }
832 
setControlVectors(sal_uInt32 nIndex,const basegfx::B2DVector & rPrev,const basegfx::B2DVector & rNext)833     void setControlVectors(sal_uInt32 nIndex, const basegfx::B2DVector& rPrev, const basegfx::B2DVector& rNext)
834     {
835         setPrevControlVector(nIndex, rPrev);
836         setNextControlVector(nIndex, rNext);
837     }
838 
appendBezierSegment(const basegfx::B2DVector & rNext,const basegfx::B2DVector & rPrev,const basegfx::B2DPoint & rPoint)839     void appendBezierSegment(const basegfx::B2DVector& rNext, const basegfx::B2DVector& rPrev, const basegfx::B2DPoint& rPoint)
840     {
841         mpBufferedData.reset();
842         const sal_uInt32 nCount(maPoints.count());
843 
844         if(nCount)
845         {
846             setNextControlVector(nCount - 1, rNext);
847         }
848 
849         insert(nCount, rPoint, 1);
850         setPrevControlVector(nCount, rPrev);
851     }
852 
insert(sal_uInt32 nIndex,const ImplB2DPolygon & rSource)853     void insert(sal_uInt32 nIndex, const ImplB2DPolygon& rSource)
854     {
855         const sal_uInt32 nCount(rSource.maPoints.count());
856 
857         if(nCount)
858         {
859             mpBufferedData.reset();
860 
861             if(rSource.mpControlVector && rSource.mpControlVector->isUsed() && !mpControlVector)
862             {
863                 mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
864             }
865 
866             maPoints.insert(nIndex, rSource.maPoints);
867 
868             if(rSource.mpControlVector)
869             {
870                 mpControlVector->insert(nIndex, *rSource.mpControlVector);
871 
872                 if(!mpControlVector->isUsed())
873                     mpControlVector.reset();
874             }
875             else if(mpControlVector)
876             {
877                 ControlVectorPair2D aVectorPair;
878                 mpControlVector->insert(nIndex, aVectorPair, nCount);
879             }
880         }
881     }
882 
remove(sal_uInt32 nIndex,sal_uInt32 nCount)883     void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
884     {
885         if(nCount)
886         {
887             mpBufferedData.reset();
888             maPoints.remove(nIndex, nCount);
889 
890             if(mpControlVector)
891             {
892                 mpControlVector->remove(nIndex, nCount);
893 
894                 if(!mpControlVector->isUsed())
895                     mpControlVector.reset();
896             }
897         }
898     }
899 
flip()900     void flip()
901     {
902         if(maPoints.count() > 1)
903         {
904             mpBufferedData.reset();
905 
906             // flip points
907             maPoints.flip(mbIsClosed);
908 
909             if(mpControlVector)
910             {
911                 // flip control vector
912                 mpControlVector->flip(mbIsClosed);
913             }
914         }
915     }
916 
hasDoublePoints() const917     bool hasDoublePoints() const
918     {
919         if(mbIsClosed)
920         {
921             // check for same start and end point
922             const sal_uInt32 nIndex(maPoints.count() - 1);
923 
924             if(maPoints.getCoordinate(0) == maPoints.getCoordinate(nIndex))
925             {
926                 if(mpControlVector)
927                 {
928                     if(mpControlVector->getNextVector(nIndex).equalZero() && mpControlVector->getPrevVector(0).equalZero())
929                     {
930                         return true;
931                     }
932                 }
933                 else
934                 {
935                     return true;
936                 }
937             }
938         }
939 
940         // test for range
941         for(sal_uInt32 a(0); a < maPoints.count() - 1; a++)
942         {
943             if(maPoints.getCoordinate(a) == maPoints.getCoordinate(a + 1))
944             {
945                 if(mpControlVector)
946                 {
947                     if(mpControlVector->getNextVector(a).equalZero() && mpControlVector->getPrevVector(a + 1).equalZero())
948                     {
949                         return true;
950                     }
951                 }
952                 else
953                 {
954                     return true;
955                 }
956             }
957         }
958 
959         return false;
960     }
961 
removeDoublePointsAtBeginEnd()962     void removeDoublePointsAtBeginEnd()
963     {
964         // Only remove DoublePoints at Begin and End when poly is closed
965         if(mbIsClosed)
966         {
967             mpBufferedData.reset();
968 
969             if(mpControlVector)
970             {
971                 bool bRemove;
972 
973                 do
974                 {
975                     bRemove = false;
976 
977                     if(maPoints.count() > 1)
978                     {
979                         const sal_uInt32 nIndex(maPoints.count() - 1);
980 
981                         if(maPoints.getCoordinate(0) == maPoints.getCoordinate(nIndex))
982                         {
983                             if(mpControlVector)
984                             {
985                                 if(mpControlVector->getNextVector(nIndex).equalZero() && mpControlVector->getPrevVector(0).equalZero())
986                                 {
987                                     bRemove = true;
988                                 }
989                             }
990                             else
991                             {
992                                 bRemove = true;
993                             }
994                         }
995                     }
996 
997                     if(bRemove)
998                     {
999                         const sal_uInt32 nIndex(maPoints.count() - 1);
1000 
1001                         if(mpControlVector && !mpControlVector->getPrevVector(nIndex).equalZero())
1002                         {
1003                             mpControlVector->setPrevVector(0, mpControlVector->getPrevVector(nIndex));
1004                         }
1005 
1006                         remove(nIndex, 1);
1007                     }
1008                 }
1009                 while(bRemove);
1010             }
1011             else
1012             {
1013                 maPoints.removeDoublePointsAtBeginEnd();
1014             }
1015         }
1016     }
1017 
removeDoublePointsWholeTrack()1018     void removeDoublePointsWholeTrack()
1019     {
1020         mpBufferedData.reset();
1021 
1022         if(mpControlVector)
1023         {
1024             sal_uInt32 nIndex(0);
1025 
1026             // test as long as there are at least two points and as long as the index
1027             // is smaller or equal second last point
1028             while((maPoints.count() > 1) && (nIndex <= maPoints.count() - 2))
1029             {
1030                 bool bRemove(maPoints.getCoordinate(nIndex) == maPoints.getCoordinate(nIndex + 1));
1031 
1032                 if(bRemove && mpControlVector)
1033                 {
1034                     if(!mpControlVector->getNextVector(nIndex).equalZero() || !mpControlVector->getPrevVector(nIndex + 1).equalZero())
1035                     {
1036                         bRemove = false;
1037                     }
1038                 }
1039 
1040                 if(bRemove)
1041                 {
1042                     if(mpControlVector && !mpControlVector->getPrevVector(nIndex).equalZero())
1043                     {
1044                         mpControlVector->setPrevVector(nIndex + 1, mpControlVector->getPrevVector(nIndex));
1045                     }
1046 
1047                     // if next is same as index and the control vectors are unused, delete index
1048                     remove(nIndex, 1);
1049                 }
1050                 else
1051                 {
1052                     // if different, step forward
1053                     nIndex++;
1054                 }
1055             }
1056         }
1057         else
1058         {
1059             maPoints.removeDoublePointsWholeTrack();
1060         }
1061     }
1062 
transform(const basegfx::B2DHomMatrix & rMatrix)1063     void transform(const basegfx::B2DHomMatrix& rMatrix)
1064     {
1065         mpBufferedData.reset();
1066 
1067         if(mpControlVector)
1068         {
1069             for(sal_uInt32 a(0); a < maPoints.count(); a++)
1070             {
1071                 basegfx::B2DPoint aCandidate = maPoints.getCoordinate(a);
1072 
1073                 if(mpControlVector->isUsed())
1074                 {
1075                     const basegfx::B2DVector& rPrevVector(mpControlVector->getPrevVector(a));
1076                     const basegfx::B2DVector& rNextVector(mpControlVector->getNextVector(a));
1077 
1078                     if(!rPrevVector.equalZero())
1079                     {
1080                         basegfx::B2DVector aPrevVector(rMatrix * rPrevVector);
1081                         mpControlVector->setPrevVector(a, aPrevVector);
1082                     }
1083 
1084                     if(!rNextVector.equalZero())
1085                     {
1086                         basegfx::B2DVector aNextVector(rMatrix * rNextVector);
1087                         mpControlVector->setNextVector(a, aNextVector);
1088                     }
1089                 }
1090 
1091                 aCandidate *= rMatrix;
1092                 maPoints.setCoordinate(a, aCandidate);
1093             }
1094 
1095             if(!mpControlVector->isUsed())
1096                 mpControlVector.reset();
1097         }
1098         else
1099         {
1100             maPoints.transform(rMatrix);
1101         }
1102     }
1103 
addOrReplaceSystemDependentData(basegfx::SystemDependentData_SharedPtr & rData)1104     void addOrReplaceSystemDependentData(basegfx::SystemDependentData_SharedPtr& rData)
1105     {
1106         if(!mpBufferedData)
1107         {
1108             mpBufferedData.reset(new ImplBufferedData);
1109         }
1110 
1111         mpBufferedData->addOrReplaceSystemDependentData(rData);
1112     }
1113 
getSystemDependentData(size_t hash_code) const1114     basegfx::SystemDependentData_SharedPtr getSystemDependentData(size_t hash_code) const
1115     {
1116         if(mpBufferedData)
1117         {
1118             return mpBufferedData->getSystemDependentData(hash_code);
1119         }
1120 
1121         return basegfx::SystemDependentData_SharedPtr();
1122     }
1123 };
1124 
1125 namespace basegfx
1126 {
1127     B2DPolygon::B2DPolygon() = default;
1128 
B2DPolygon(std::initializer_list<basegfx::B2DPoint> aPoints)1129     B2DPolygon::B2DPolygon(std::initializer_list<basegfx::B2DPoint> aPoints)
1130         : mpPolygon()
1131     {
1132         for (const basegfx::B2DPoint& rPoint : aPoints)
1133         {
1134             append(rPoint);
1135         }
1136     }
1137 
1138     B2DPolygon::B2DPolygon(const B2DPolygon&) = default;
1139 
1140     B2DPolygon::B2DPolygon(B2DPolygon&&) = default;
1141 
B2DPolygon(const B2DPolygon & rPolygon,sal_uInt32 nIndex,sal_uInt32 nCount)1142     B2DPolygon::B2DPolygon(const B2DPolygon& rPolygon, sal_uInt32 nIndex, sal_uInt32 nCount)
1143     :   mpPolygon(ImplB2DPolygon(*rPolygon.mpPolygon, nIndex, nCount))
1144     {
1145         // TODO(P2): one extra temporary here (cow_wrapper copies
1146         // given ImplB2DPolygon into its internal impl_t wrapper type)
1147         OSL_ENSURE(nIndex + nCount <= rPolygon.mpPolygon->count(), "B2DPolygon constructor outside range (!)");
1148     }
1149 
1150     B2DPolygon::~B2DPolygon() = default;
1151 
1152     B2DPolygon& B2DPolygon::operator=(const B2DPolygon&) = default;
1153 
1154     B2DPolygon& B2DPolygon::operator=(B2DPolygon&&) = default;
1155 
makeUnique()1156     void B2DPolygon::makeUnique()
1157     {
1158         mpPolygon.make_unique();
1159     }
1160 
operator ==(const B2DPolygon & rPolygon) const1161     bool B2DPolygon::operator==(const B2DPolygon& rPolygon) const
1162     {
1163         if(mpPolygon.same_object(rPolygon.mpPolygon))
1164             return true;
1165 
1166         return ((*mpPolygon) == (*rPolygon.mpPolygon));
1167     }
1168 
operator !=(const B2DPolygon & rPolygon) const1169     bool B2DPolygon::operator!=(const B2DPolygon& rPolygon) const
1170     {
1171         return !(*this == rPolygon);
1172     }
1173 
count() const1174     sal_uInt32 B2DPolygon::count() const
1175     {
1176         return mpPolygon->count();
1177     }
1178 
getB2DPoint(sal_uInt32 nIndex) const1179     B2DPoint const & B2DPolygon::getB2DPoint(sal_uInt32 nIndex) const
1180     {
1181         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1182 
1183         return mpPolygon->getPoint(nIndex);
1184     }
1185 
setB2DPoint(sal_uInt32 nIndex,const B2DPoint & rValue)1186     void B2DPolygon::setB2DPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
1187     {
1188         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1189 
1190         if(mpPolygon->getPoint(nIndex) != rValue)
1191         {
1192             mpPolygon->setPoint(nIndex, rValue);
1193         }
1194     }
1195 
reserve(sal_uInt32 nCount)1196     void B2DPolygon::reserve(sal_uInt32 nCount)
1197     {
1198         mpPolygon->reserve(nCount);
1199     }
1200 
insert(sal_uInt32 nIndex,const B2DPoint & rPoint,sal_uInt32 nCount)1201     void B2DPolygon::insert(sal_uInt32 nIndex, const B2DPoint& rPoint, sal_uInt32 nCount)
1202     {
1203         OSL_ENSURE(nIndex <= mpPolygon->count(), "B2DPolygon Insert outside range (!)");
1204 
1205         if(nCount)
1206         {
1207             mpPolygon->insert(nIndex, rPoint, nCount);
1208         }
1209     }
1210 
append(const B2DPoint & rPoint,sal_uInt32 nCount)1211     void B2DPolygon::append(const B2DPoint& rPoint, sal_uInt32 nCount)
1212     {
1213         if(nCount)
1214         {
1215             mpPolygon->insert(mpPolygon->count(), rPoint, nCount);
1216         }
1217     }
1218 
append(const B2DPoint & rPoint)1219     void B2DPolygon::append(const B2DPoint& rPoint)
1220     {
1221         mpPolygon->append(rPoint);
1222     }
1223 
getPrevControlPoint(sal_uInt32 nIndex) const1224     B2DPoint B2DPolygon::getPrevControlPoint(sal_uInt32 nIndex) const
1225     {
1226         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1227 
1228         if(mpPolygon->areControlPointsUsed())
1229         {
1230             return mpPolygon->getPoint(nIndex) + mpPolygon->getPrevControlVector(nIndex);
1231         }
1232         else
1233         {
1234             return mpPolygon->getPoint(nIndex);
1235         }
1236     }
1237 
getNextControlPoint(sal_uInt32 nIndex) const1238     B2DPoint B2DPolygon::getNextControlPoint(sal_uInt32 nIndex) const
1239     {
1240         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1241 
1242         if(mpPolygon->areControlPointsUsed())
1243         {
1244             return mpPolygon->getPoint(nIndex) + mpPolygon->getNextControlVector(nIndex);
1245         }
1246         else
1247         {
1248             return mpPolygon->getPoint(nIndex);
1249         }
1250     }
1251 
setPrevControlPoint(sal_uInt32 nIndex,const B2DPoint & rValue)1252     void B2DPolygon::setPrevControlPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
1253     {
1254         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1255         const basegfx::B2DVector aNewVector(rValue - mpPolygon->getPoint(nIndex));
1256 
1257         if(mpPolygon->getPrevControlVector(nIndex) != aNewVector)
1258         {
1259             mpPolygon->setPrevControlVector(nIndex, aNewVector);
1260         }
1261     }
1262 
setNextControlPoint(sal_uInt32 nIndex,const B2DPoint & rValue)1263     void B2DPolygon::setNextControlPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
1264     {
1265         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1266         const basegfx::B2DVector aNewVector(rValue - mpPolygon->getPoint(nIndex));
1267 
1268         if(mpPolygon->getNextControlVector(nIndex) != aNewVector)
1269         {
1270             mpPolygon->setNextControlVector(nIndex, aNewVector);
1271         }
1272     }
1273 
setControlPoints(sal_uInt32 nIndex,const basegfx::B2DPoint & rPrev,const basegfx::B2DPoint & rNext)1274     void B2DPolygon::setControlPoints(sal_uInt32 nIndex, const basegfx::B2DPoint& rPrev, const basegfx::B2DPoint& rNext)
1275     {
1276         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1277         const B2DPoint aPoint(mpPolygon->getPoint(nIndex));
1278         const basegfx::B2DVector aNewPrev(rPrev - aPoint);
1279         const basegfx::B2DVector aNewNext(rNext - aPoint);
1280 
1281         if(mpPolygon->getPrevControlVector(nIndex) != aNewPrev || mpPolygon->getNextControlVector(nIndex) != aNewNext)
1282         {
1283             mpPolygon->setControlVectors(nIndex, aNewPrev, aNewNext);
1284         }
1285     }
1286 
resetPrevControlPoint(sal_uInt32 nIndex)1287     void B2DPolygon::resetPrevControlPoint(sal_uInt32 nIndex)
1288     {
1289         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1290 
1291         if(mpPolygon->areControlPointsUsed() && !mpPolygon->getPrevControlVector(nIndex).equalZero())
1292         {
1293             mpPolygon->setPrevControlVector(nIndex, B2DVector::getEmptyVector());
1294         }
1295     }
1296 
resetNextControlPoint(sal_uInt32 nIndex)1297     void B2DPolygon::resetNextControlPoint(sal_uInt32 nIndex)
1298     {
1299         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1300 
1301         if(mpPolygon->areControlPointsUsed() && !mpPolygon->getNextControlVector(nIndex).equalZero())
1302         {
1303             mpPolygon->setNextControlVector(nIndex, B2DVector::getEmptyVector());
1304         }
1305     }
1306 
resetControlPoints()1307     void B2DPolygon::resetControlPoints()
1308     {
1309         if(mpPolygon->areControlPointsUsed())
1310         {
1311             mpPolygon->resetControlVectors();
1312         }
1313     }
1314 
appendBezierSegment(const B2DPoint & rNextControlPoint,const B2DPoint & rPrevControlPoint,const B2DPoint & rPoint)1315     void B2DPolygon::appendBezierSegment(
1316         const B2DPoint& rNextControlPoint,
1317         const B2DPoint& rPrevControlPoint,
1318         const B2DPoint& rPoint)
1319     {
1320         const B2DVector aNewNextVector(mpPolygon->count() ? B2DVector(rNextControlPoint - mpPolygon->getPoint(mpPolygon->count() - 1)) : B2DVector::getEmptyVector());
1321         const B2DVector aNewPrevVector(rPrevControlPoint - rPoint);
1322 
1323         if(aNewNextVector.equalZero() && aNewPrevVector.equalZero())
1324         {
1325             mpPolygon->insert(mpPolygon->count(), rPoint, 1);
1326         }
1327         else
1328         {
1329             mpPolygon->appendBezierSegment(aNewNextVector, aNewPrevVector, rPoint);
1330         }
1331     }
1332 
appendQuadraticBezierSegment(const B2DPoint & rControlPoint,const B2DPoint & rPoint)1333     void B2DPolygon::appendQuadraticBezierSegment(const B2DPoint& rControlPoint, const B2DPoint& rPoint)
1334     {
1335         if (mpPolygon->count() == 0)
1336         {
1337             mpPolygon->append(rPoint);
1338             const double nX((rControlPoint.getX() * 2.0 + rPoint.getX()) / 3.0);
1339             const double nY((rControlPoint.getY() * 2.0 + rPoint.getY()) / 3.0);
1340             setPrevControlPoint(0, B2DPoint(nX, nY));
1341         }
1342         else
1343         {
1344             const B2DPoint aPreviousPoint(mpPolygon->getPoint(mpPolygon->count() - 1));
1345 
1346             const double nX1((rControlPoint.getX() * 2.0 + aPreviousPoint.getX()) / 3.0);
1347             const double nY1((rControlPoint.getY() * 2.0 + aPreviousPoint.getY()) / 3.0);
1348             const double nX2((rControlPoint.getX() * 2.0 + rPoint.getX()) / 3.0);
1349             const double nY2((rControlPoint.getY() * 2.0 + rPoint.getY()) / 3.0);
1350 
1351             appendBezierSegment(B2DPoint(nX1, nY1), B2DPoint(nX2, nY2), rPoint);
1352         }
1353     }
1354 
areControlPointsUsed() const1355     bool B2DPolygon::areControlPointsUsed() const
1356     {
1357         return mpPolygon->areControlPointsUsed();
1358     }
1359 
isPrevControlPointUsed(sal_uInt32 nIndex) const1360     bool B2DPolygon::isPrevControlPointUsed(sal_uInt32 nIndex) const
1361     {
1362         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1363 
1364         return (mpPolygon->areControlPointsUsed() && !mpPolygon->getPrevControlVector(nIndex).equalZero());
1365     }
1366 
isNextControlPointUsed(sal_uInt32 nIndex) const1367     bool B2DPolygon::isNextControlPointUsed(sal_uInt32 nIndex) const
1368     {
1369         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1370 
1371         return (mpPolygon->areControlPointsUsed() && !mpPolygon->getNextControlVector(nIndex).equalZero());
1372     }
1373 
getContinuityInPoint(sal_uInt32 nIndex) const1374     B2VectorContinuity B2DPolygon::getContinuityInPoint(sal_uInt32 nIndex) const
1375     {
1376         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1377 
1378         if(mpPolygon->areControlPointsUsed())
1379         {
1380             const B2DVector& rPrev(mpPolygon->getPrevControlVector(nIndex));
1381             const B2DVector& rNext(mpPolygon->getNextControlVector(nIndex));
1382 
1383             return getContinuity(rPrev, rNext);
1384         }
1385         else
1386         {
1387             return B2VectorContinuity::NONE;
1388         }
1389     }
1390 
getBezierSegment(sal_uInt32 nIndex,B2DCubicBezier & rTarget) const1391     void B2DPolygon::getBezierSegment(sal_uInt32 nIndex, B2DCubicBezier& rTarget) const
1392     {
1393         OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1394         const bool bNextIndexValidWithoutClose(nIndex + 1 < mpPolygon->count());
1395 
1396         if(bNextIndexValidWithoutClose || mpPolygon->isClosed())
1397         {
1398             const sal_uInt32 nNextIndex(bNextIndexValidWithoutClose ? nIndex + 1 : 0);
1399             rTarget.setStartPoint(mpPolygon->getPoint(nIndex));
1400             rTarget.setEndPoint(mpPolygon->getPoint(nNextIndex));
1401 
1402             if(mpPolygon->areControlPointsUsed())
1403             {
1404                 rTarget.setControlPointA(rTarget.getStartPoint() + mpPolygon->getNextControlVector(nIndex));
1405                 rTarget.setControlPointB(rTarget.getEndPoint() + mpPolygon->getPrevControlVector(nNextIndex));
1406             }
1407             else
1408             {
1409                 // no bezier, reset control points at rTarget
1410                 rTarget.setControlPointA(rTarget.getStartPoint());
1411                 rTarget.setControlPointB(rTarget.getEndPoint());
1412             }
1413         }
1414         else
1415         {
1416             // no valid edge at all, reset rTarget to current point
1417             const B2DPoint aPoint(mpPolygon->getPoint(nIndex));
1418             rTarget.setStartPoint(aPoint);
1419             rTarget.setEndPoint(aPoint);
1420             rTarget.setControlPointA(aPoint);
1421             rTarget.setControlPointB(aPoint);
1422         }
1423     }
1424 
getDefaultAdaptiveSubdivision() const1425     B2DPolygon const & B2DPolygon::getDefaultAdaptiveSubdivision() const
1426     {
1427         return mpPolygon->getDefaultAdaptiveSubdivision(*this);
1428     }
1429 
getB2DRange() const1430     B2DRange const & B2DPolygon::getB2DRange() const
1431     {
1432         return mpPolygon->getB2DRange(*this);
1433     }
1434 
append(const B2DPolygon & rPoly,sal_uInt32 nIndex,sal_uInt32 nCount)1435     void B2DPolygon::append(const B2DPolygon& rPoly, sal_uInt32 nIndex, sal_uInt32 nCount)
1436     {
1437         if(rPoly.count())
1438         {
1439             if(!nCount)
1440             {
1441                 nCount = rPoly.count();
1442             }
1443 
1444             if(nIndex == 0 && nCount == rPoly.count())
1445             {
1446                 mpPolygon->insert(mpPolygon->count(), *rPoly.mpPolygon);
1447             }
1448             else
1449             {
1450                 OSL_ENSURE(nIndex + nCount <= rPoly.mpPolygon->count(), "B2DPolygon Append outside range (!)");
1451                 ImplB2DPolygon aTempPoly(*rPoly.mpPolygon, nIndex, nCount);
1452                 mpPolygon->insert(mpPolygon->count(), aTempPoly);
1453             }
1454         }
1455     }
1456 
remove(sal_uInt32 nIndex,sal_uInt32 nCount)1457     void B2DPolygon::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
1458     {
1459         OSL_ENSURE(nIndex + nCount <= mpPolygon->count(), "B2DPolygon Remove outside range (!)");
1460 
1461         if(nCount)
1462         {
1463             mpPolygon->remove(nIndex, nCount);
1464         }
1465     }
1466 
clear()1467     void B2DPolygon::clear()
1468     {
1469         *mpPolygon = ImplB2DPolygon();
1470     }
1471 
isClosed() const1472     bool B2DPolygon::isClosed() const
1473     {
1474         return mpPolygon->isClosed();
1475     }
1476 
setClosed(bool bNew)1477     void B2DPolygon::setClosed(bool bNew)
1478     {
1479         if(isClosed() != bNew)
1480         {
1481             mpPolygon->setClosed(bNew);
1482         }
1483     }
1484 
flip()1485     void B2DPolygon::flip()
1486     {
1487         if(count() > 1)
1488         {
1489             mpPolygon->flip();
1490         }
1491     }
1492 
hasDoublePoints() const1493     bool B2DPolygon::hasDoublePoints() const
1494     {
1495         return (mpPolygon->count() > 1 && mpPolygon->hasDoublePoints());
1496     }
1497 
removeDoublePoints()1498     void B2DPolygon::removeDoublePoints()
1499     {
1500         if(hasDoublePoints())
1501         {
1502             mpPolygon->removeDoublePointsAtBeginEnd();
1503             mpPolygon->removeDoublePointsWholeTrack();
1504         }
1505     }
1506 
transform(const B2DHomMatrix & rMatrix)1507     void B2DPolygon::transform(const B2DHomMatrix& rMatrix)
1508     {
1509         if(mpPolygon->count() && !rMatrix.isIdentity())
1510         {
1511             mpPolygon->transform(rMatrix);
1512         }
1513     }
1514 
addOrReplaceSystemDependentDataInternal(SystemDependentData_SharedPtr & rData) const1515     void B2DPolygon::addOrReplaceSystemDependentDataInternal(SystemDependentData_SharedPtr& rData) const
1516     {
1517         // Need to get ImplB2DPolygon* from cow_wrapper *without*
1518         // calling make_unique() here - we do not want to
1519         // 'modify' the ImplB2DPolygon, but add buffered data that
1520         // is valid for all referencing instances
1521         const B2DPolygon* pMe(this);
1522         const ImplB2DPolygon* pMyImpl(pMe->mpPolygon.get());
1523 
1524         const_cast<ImplB2DPolygon*>(pMyImpl)->addOrReplaceSystemDependentData(rData);
1525     }
1526 
getSystemDependantDataInternal(size_t hash_code) const1527     SystemDependentData_SharedPtr B2DPolygon::getSystemDependantDataInternal(size_t hash_code) const
1528     {
1529         return mpPolygon->getSystemDependentData(hash_code);
1530     }
1531 
1532 } // end of namespace basegfx
1533 
1534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1535