Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / basegfx / source / polygon / b2dpolygon.cxx
blob87343028ecfed356d3b65d8241ce20aabb644012
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
32 struct CoordinateData2D : public basegfx::B2DPoint
34 public:
35 CoordinateData2D() {}
37 explicit CoordinateData2D(const basegfx::B2DPoint& rData)
38 : B2DPoint(rData)
41 CoordinateData2D& operator=(const basegfx::B2DPoint& rData)
43 B2DPoint::operator=(rData);
44 return *this;
47 void transform(const basegfx::B2DHomMatrix& rMatrix)
49 *this *= rMatrix;
53 class CoordinateDataArray2D
55 typedef std::vector< CoordinateData2D > CoordinateData2DVector;
57 CoordinateData2DVector maVector;
59 public:
60 explicit CoordinateDataArray2D(sal_uInt32 nCount)
61 : maVector(nCount)
65 CoordinateDataArray2D(const CoordinateDataArray2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
66 : maVector(rOriginal.maVector.begin() + nIndex, rOriginal.maVector.begin() + (nIndex + nCount))
70 sal_uInt32 count() const
72 return maVector.size();
75 bool operator==(const CoordinateDataArray2D& rCandidate) const
77 return (maVector == rCandidate.maVector);
80 const basegfx::B2DPoint& getCoordinate(sal_uInt32 nIndex) const
82 return maVector[nIndex];
85 void setCoordinate(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue)
87 maVector[nIndex] = rValue;
90 void reserve(sal_uInt32 nCount)
92 maVector.reserve(nCount);
95 void append(const CoordinateData2D& rValue)
97 maVector.push_back(rValue);
100 void insert(sal_uInt32 nIndex, const CoordinateData2D& rValue, sal_uInt32 nCount)
102 if(nCount)
104 // add nCount copies of rValue
105 CoordinateData2DVector::iterator aIndex(maVector.begin());
106 aIndex += nIndex;
107 maVector.insert(aIndex, nCount, rValue);
111 void insert(sal_uInt32 nIndex, const CoordinateDataArray2D& rSource)
113 const sal_uInt32 nCount(rSource.maVector.size());
115 if(nCount)
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);
126 void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
128 if(nCount)
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);
138 void flip(bool bIsClosed)
140 if(maVector.size() > 1)
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);
148 for(sal_uInt32 a(0); a < nHalfSize; a++)
150 std::swap(*aStart, *aEnd);
151 ++aStart;
152 --aEnd;
157 void removeDoublePointsAtBeginEnd()
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]))
163 maVector.pop_back();
167 void removeDoublePointsWholeTrack()
169 sal_uInt32 nIndex(0);
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))
175 if(maVector[nIndex] == maVector[nIndex + 1])
177 // if next is same as index, delete next
178 maVector.erase(maVector.begin() + (nIndex + 1));
180 else
182 // if different, step forward
183 nIndex++;
188 void transform(const basegfx::B2DHomMatrix& rMatrix)
190 for (auto & elem : maVector)
192 elem.transform(rMatrix);
197 class ControlVectorPair2D
199 basegfx::B2DVector maPrevVector;
200 basegfx::B2DVector maNextVector;
202 public:
203 explicit ControlVectorPair2D() {}
205 const basegfx::B2DVector& getPrevVector() const
207 return maPrevVector;
210 void setPrevVector(const basegfx::B2DVector& rValue)
212 if(rValue != maPrevVector)
213 maPrevVector = rValue;
216 const basegfx::B2DVector& getNextVector() const
218 return maNextVector;
221 void setNextVector(const basegfx::B2DVector& rValue)
223 if(rValue != maNextVector)
224 maNextVector = rValue;
227 bool operator==(const ControlVectorPair2D& rData) const
229 return (maPrevVector == rData.getPrevVector() && maNextVector == rData.getNextVector());
232 void flip()
234 std::swap(maPrevVector, maNextVector);
238 class ControlVectorArray2D
240 typedef std::vector< ControlVectorPair2D > ControlVectorPair2DVector;
242 ControlVectorPair2DVector maVector;
243 sal_uInt32 mnUsedVectors;
245 public:
246 explicit ControlVectorArray2D(sal_uInt32 nCount)
247 : maVector(nCount),
248 mnUsedVectors(0)
251 ControlVectorArray2D(const ControlVectorArray2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
252 : maVector(),
253 mnUsedVectors(0)
255 ControlVectorPair2DVector::const_iterator aStart(rOriginal.maVector.begin());
256 aStart += nIndex;
257 ControlVectorPair2DVector::const_iterator aEnd(aStart);
258 aEnd += nCount;
259 maVector.reserve(nCount);
261 for(; aStart != aEnd; ++aStart)
263 if(!aStart->getPrevVector().equalZero())
264 mnUsedVectors++;
266 if(!aStart->getNextVector().equalZero())
267 mnUsedVectors++;
269 maVector.push_back(*aStart);
273 bool operator==(const ControlVectorArray2D& rCandidate) const
275 return (maVector == rCandidate.maVector);
278 bool isUsed() const
280 return (mnUsedVectors != 0);
283 const basegfx::B2DVector& getPrevVector(sal_uInt32 nIndex) const
285 return maVector[nIndex].getPrevVector();
288 void setPrevVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
290 bool bWasUsed(mnUsedVectors && !maVector[nIndex].getPrevVector().equalZero());
291 bool bIsUsed(!rValue.equalZero());
293 if(bWasUsed)
295 if(bIsUsed)
297 maVector[nIndex].setPrevVector(rValue);
299 else
301 maVector[nIndex].setPrevVector(basegfx::B2DVector::getEmptyVector());
302 mnUsedVectors--;
305 else
307 if(bIsUsed)
309 maVector[nIndex].setPrevVector(rValue);
310 mnUsedVectors++;
315 const basegfx::B2DVector& getNextVector(sal_uInt32 nIndex) const
317 return maVector[nIndex].getNextVector();
320 void setNextVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
322 bool bWasUsed(mnUsedVectors && !maVector[nIndex].getNextVector().equalZero());
323 bool bIsUsed(!rValue.equalZero());
325 if(bWasUsed)
327 if(bIsUsed)
329 maVector[nIndex].setNextVector(rValue);
331 else
333 maVector[nIndex].setNextVector(basegfx::B2DVector::getEmptyVector());
334 mnUsedVectors--;
337 else
339 if(bIsUsed)
341 maVector[nIndex].setNextVector(rValue);
342 mnUsedVectors++;
347 void append(const ControlVectorPair2D& rValue)
349 maVector.push_back(rValue);
351 if(!rValue.getPrevVector().equalZero())
352 mnUsedVectors += 1;
354 if(!rValue.getNextVector().equalZero())
355 mnUsedVectors += 1;
358 void insert(sal_uInt32 nIndex, const ControlVectorPair2D& rValue, sal_uInt32 nCount)
360 if(nCount)
362 // add nCount copies of rValue
363 ControlVectorPair2DVector::iterator aIndex(maVector.begin());
364 aIndex += nIndex;
365 maVector.insert(aIndex, nCount, rValue);
367 if(!rValue.getPrevVector().equalZero())
368 mnUsedVectors += nCount;
370 if(!rValue.getNextVector().equalZero())
371 mnUsedVectors += nCount;
375 void insert(sal_uInt32 nIndex, const ControlVectorArray2D& rSource)
377 const sal_uInt32 nCount(rSource.maVector.size());
379 if(nCount)
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);
388 for(; aStart != aEnd; ++aStart)
390 if(!aStart->getPrevVector().equalZero())
391 mnUsedVectors++;
393 if(!aStart->getNextVector().equalZero())
394 mnUsedVectors++;
399 void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
401 if(nCount)
403 const ControlVectorPair2DVector::iterator aDeleteStart(maVector.begin() + nIndex);
404 const ControlVectorPair2DVector::iterator aDeleteEnd(aDeleteStart + nCount);
405 ControlVectorPair2DVector::const_iterator aStart(aDeleteStart);
407 for(; mnUsedVectors && aStart != aDeleteEnd; ++aStart)
409 if(!aStart->getPrevVector().equalZero())
410 mnUsedVectors--;
412 if(mnUsedVectors && !aStart->getNextVector().equalZero())
413 mnUsedVectors--;
416 // remove point data
417 maVector.erase(aDeleteStart, aDeleteEnd);
421 void flip(bool bIsClosed)
423 if(maVector.size() > 1)
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);
431 for(sal_uInt32 a(0); a < nHalfSize; a++)
433 // swap Prev and Next
434 aStart->flip();
435 aEnd->flip();
437 // swap entries
438 std::swap(*aStart, *aEnd);
440 ++aStart;
441 --aEnd;
444 if(aStart == aEnd)
446 // swap Prev and Next at middle element (if exists)
447 aStart->flip();
450 if(bIsClosed)
452 // swap Prev and Next at start element
453 maVector.begin()->flip();
459 class ImplBufferedData : public basegfx::SystemDependentDataHolder
461 private:
462 // Possibility to hold the last subdivision
463 std::unique_ptr< basegfx::B2DPolygon > mpDefaultSubdivision;
465 // Possibility to hold the last B2DRange calculation
466 std::unique_ptr< basegfx::B2DRange > mpB2DRange;
468 public:
469 ImplBufferedData()
470 : basegfx::SystemDependentDataHolder(),
471 mpDefaultSubdivision(),
472 mpB2DRange()
476 const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
478 if(!mpDefaultSubdivision)
480 const_cast< ImplBufferedData* >(this)->mpDefaultSubdivision.reset(new basegfx::B2DPolygon(basegfx::utils::adaptiveSubdivideByAngle(rSource)));
483 return *mpDefaultSubdivision;
486 const basegfx::B2DRange& getB2DRange(const basegfx::B2DPolygon& rSource) const
488 if(!mpB2DRange)
490 basegfx::B2DRange aNewRange;
491 const sal_uInt32 nPointCount(rSource.count());
493 if(nPointCount)
495 for(sal_uInt32 a(0); a < nPointCount; a++)
497 aNewRange.expand(rSource.getB2DPoint(a));
500 if(rSource.areControlPointsUsed())
502 const sal_uInt32 nEdgeCount(rSource.isClosed() ? nPointCount : nPointCount - 1);
504 if(nEdgeCount)
506 basegfx::B2DCubicBezier aEdge;
507 aEdge.setStartPoint(rSource.getB2DPoint(0));
509 for(sal_uInt32 b(0); b < nEdgeCount; b++)
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));
516 if(aEdge.isBezier())
518 const basegfx::B2DRange aBezierRangeWithControlPoints(aEdge.getRange());
520 if(!aNewRange.isInside(aBezierRangeWithControlPoints))
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;
529 aExtremas.reserve(4);
530 aEdge.getAllExtremumPositions(aExtremas);
532 const sal_uInt32 nExtremaCount(aExtremas.size());
534 for(sal_uInt32 c(0); c < nExtremaCount; c++)
536 aNewRange.expand(aEdge.interpolatePoint(aExtremas[c]));
541 // prepare next edge
542 aEdge.setStartPoint(aEdge.getEndPoint());
548 const_cast< ImplBufferedData* >(this)->mpB2DRange.reset(new basegfx::B2DRange(aNewRange));
551 return *mpB2DRange;
555 class ImplB2DPolygon
557 private:
558 // The point vector. This vector exists always and defines the
559 // count of members.
560 CoordinateDataArray2D maPoints;
562 // The control point vectors. This vectors are created on demand
563 // and may be zero.
564 std::unique_ptr< ControlVectorArray2D > mpControlVector;
566 // buffered data for e.g. default subdivision and range
567 std::unique_ptr< ImplBufferedData > mpBufferedData;
569 // flag which decides if this polygon is opened or closed
570 bool mbIsClosed;
572 public:
573 const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
575 if(!mpControlVector || !mpControlVector->isUsed())
577 return rSource;
580 if(!mpBufferedData)
582 const_cast< ImplB2DPolygon* >(this)->mpBufferedData.reset(new ImplBufferedData);
585 return mpBufferedData->getDefaultAdaptiveSubdivision(rSource);
588 const basegfx::B2DRange& getB2DRange(const basegfx::B2DPolygon& rSource) const
590 if(!mpBufferedData)
592 const_cast< ImplB2DPolygon* >(this)->mpBufferedData.reset(new ImplBufferedData);
595 return mpBufferedData->getB2DRange(rSource);
598 ImplB2DPolygon()
599 : maPoints(0),
600 mpControlVector(),
601 mpBufferedData(),
602 mbIsClosed(false)
605 ImplB2DPolygon(const ImplB2DPolygon& rToBeCopied)
606 : maPoints(rToBeCopied.maPoints),
607 mpControlVector(),
608 mpBufferedData(),
609 mbIsClosed(rToBeCopied.mbIsClosed)
611 // complete initialization using copy
612 if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
614 mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector) );
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)
624 // complete initialization using partly copy
625 if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
627 mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector, nIndex, nCount) );
629 if(!mpControlVector->isUsed())
630 mpControlVector.reset();
634 ImplB2DPolygon& operator=(const ImplB2DPolygon& rOther)
636 if (this != &rOther)
638 mpControlVector.reset();
639 mpBufferedData.reset();
640 maPoints = rOther.maPoints;
641 mbIsClosed = rOther.mbIsClosed;
642 if (rOther.mpControlVector && rOther.mpControlVector->isUsed())
644 mpControlVector.reset( new ControlVectorArray2D(*rOther.mpControlVector) );
646 if(!mpControlVector->isUsed())
647 mpControlVector.reset();
650 return *this;
653 sal_uInt32 count() const
655 return maPoints.count();
658 bool isClosed() const
660 return mbIsClosed;
663 void setClosed(bool bNew)
665 if(bNew != mbIsClosed)
667 mpBufferedData.reset();
668 mbIsClosed = bNew;
672 bool operator==(const ImplB2DPolygon& rCandidate) const
674 if(mbIsClosed == rCandidate.mbIsClosed)
676 if(maPoints == rCandidate.maPoints)
678 bool bControlVectorsAreEqual(true);
680 if(mpControlVector)
682 if(rCandidate.mpControlVector)
684 bControlVectorsAreEqual = ((*mpControlVector) == (*rCandidate.mpControlVector));
686 else
688 // candidate has no control vector, so it's assumed all unused.
689 bControlVectorsAreEqual = !mpControlVector->isUsed();
692 else
694 if(rCandidate.mpControlVector)
696 // we have no control vector, so it's assumed all unused.
697 bControlVectorsAreEqual = !rCandidate.mpControlVector->isUsed();
701 if(bControlVectorsAreEqual)
703 return true;
708 return false;
711 const basegfx::B2DPoint& getPoint(sal_uInt32 nIndex) const
713 return maPoints.getCoordinate(nIndex);
716 void setPoint(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue)
718 mpBufferedData.reset();
719 maPoints.setCoordinate(nIndex, rValue);
722 void reserve(sal_uInt32 nCount)
724 maPoints.reserve(nCount);
727 void append(const basegfx::B2DPoint& rPoint)
729 mpBufferedData.reset(); // TODO: is this needed?
730 const CoordinateData2D aCoordinate(rPoint);
731 maPoints.append(aCoordinate);
733 if(mpControlVector)
735 const ControlVectorPair2D aVectorPair;
736 mpControlVector->append(aVectorPair);
740 void insert(sal_uInt32 nIndex, const basegfx::B2DPoint& rPoint, sal_uInt32 nCount)
742 if(nCount)
744 mpBufferedData.reset();
745 CoordinateData2D aCoordinate(rPoint);
746 maPoints.insert(nIndex, aCoordinate, nCount);
748 if(mpControlVector)
750 ControlVectorPair2D aVectorPair;
751 mpControlVector->insert(nIndex, aVectorPair, nCount);
756 const basegfx::B2DVector& getPrevControlVector(sal_uInt32 nIndex) const
758 if(mpControlVector)
760 return mpControlVector->getPrevVector(nIndex);
762 else
764 return basegfx::B2DVector::getEmptyVector();
768 void setPrevControlVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
770 if(!mpControlVector)
772 if(!rValue.equalZero())
774 mpBufferedData.reset();
775 mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
776 mpControlVector->setPrevVector(nIndex, rValue);
779 else
781 mpBufferedData.reset();
782 mpControlVector->setPrevVector(nIndex, rValue);
784 if(!mpControlVector->isUsed())
785 mpControlVector.reset();
789 const basegfx::B2DVector& getNextControlVector(sal_uInt32 nIndex) const
791 if(mpControlVector)
793 return mpControlVector->getNextVector(nIndex);
795 else
797 return basegfx::B2DVector::getEmptyVector();
801 void setNextControlVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
803 if(!mpControlVector)
805 if(!rValue.equalZero())
807 mpBufferedData.reset();
808 mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
809 mpControlVector->setNextVector(nIndex, rValue);
812 else
814 mpBufferedData.reset();
815 mpControlVector->setNextVector(nIndex, rValue);
817 if(!mpControlVector->isUsed())
818 mpControlVector.reset();
822 bool areControlPointsUsed() const
824 return (mpControlVector && mpControlVector->isUsed());
827 void resetControlVectors()
829 mpBufferedData.reset();
830 mpControlVector.reset();
833 void setControlVectors(sal_uInt32 nIndex, const basegfx::B2DVector& rPrev, const basegfx::B2DVector& rNext)
835 setPrevControlVector(nIndex, rPrev);
836 setNextControlVector(nIndex, rNext);
839 void appendBezierSegment(const basegfx::B2DVector& rNext, const basegfx::B2DVector& rPrev, const basegfx::B2DPoint& rPoint)
841 mpBufferedData.reset();
842 const sal_uInt32 nCount(maPoints.count());
844 if(nCount)
846 setNextControlVector(nCount - 1, rNext);
849 insert(nCount, rPoint, 1);
850 setPrevControlVector(nCount, rPrev);
853 void insert(sal_uInt32 nIndex, const ImplB2DPolygon& rSource)
855 const sal_uInt32 nCount(rSource.maPoints.count());
857 if(nCount)
859 mpBufferedData.reset();
861 if(rSource.mpControlVector && rSource.mpControlVector->isUsed() && !mpControlVector)
863 mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
866 maPoints.insert(nIndex, rSource.maPoints);
868 if(rSource.mpControlVector)
870 mpControlVector->insert(nIndex, *rSource.mpControlVector);
872 if(!mpControlVector->isUsed())
873 mpControlVector.reset();
875 else if(mpControlVector)
877 ControlVectorPair2D aVectorPair;
878 mpControlVector->insert(nIndex, aVectorPair, nCount);
883 void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
885 if(nCount)
887 mpBufferedData.reset();
888 maPoints.remove(nIndex, nCount);
890 if(mpControlVector)
892 mpControlVector->remove(nIndex, nCount);
894 if(!mpControlVector->isUsed())
895 mpControlVector.reset();
900 void flip()
902 if(maPoints.count() > 1)
904 mpBufferedData.reset();
906 // flip points
907 maPoints.flip(mbIsClosed);
909 if(mpControlVector)
911 // flip control vector
912 mpControlVector->flip(mbIsClosed);
917 bool hasDoublePoints() const
919 if(mbIsClosed)
921 // check for same start and end point
922 const sal_uInt32 nIndex(maPoints.count() - 1);
924 if(maPoints.getCoordinate(0) == maPoints.getCoordinate(nIndex))
926 if(mpControlVector)
928 if(mpControlVector->getNextVector(nIndex).equalZero() && mpControlVector->getPrevVector(0).equalZero())
930 return true;
933 else
935 return true;
940 // test for range
941 for(sal_uInt32 a(0); a < maPoints.count() - 1; a++)
943 if(maPoints.getCoordinate(a) == maPoints.getCoordinate(a + 1))
945 if(mpControlVector)
947 if(mpControlVector->getNextVector(a).equalZero() && mpControlVector->getPrevVector(a + 1).equalZero())
949 return true;
952 else
954 return true;
959 return false;
962 void removeDoublePointsAtBeginEnd()
964 // Only remove DoublePoints at Begin and End when poly is closed
965 if(mbIsClosed)
967 mpBufferedData.reset();
969 if(mpControlVector)
971 bool bRemove;
975 bRemove = false;
977 if(maPoints.count() > 1)
979 const sal_uInt32 nIndex(maPoints.count() - 1);
981 if(maPoints.getCoordinate(0) == maPoints.getCoordinate(nIndex))
983 if(mpControlVector)
985 if(mpControlVector->getNextVector(nIndex).equalZero() && mpControlVector->getPrevVector(0).equalZero())
987 bRemove = true;
990 else
992 bRemove = true;
997 if(bRemove)
999 const sal_uInt32 nIndex(maPoints.count() - 1);
1001 if(mpControlVector && !mpControlVector->getPrevVector(nIndex).equalZero())
1003 mpControlVector->setPrevVector(0, mpControlVector->getPrevVector(nIndex));
1006 remove(nIndex, 1);
1009 while(bRemove);
1011 else
1013 maPoints.removeDoublePointsAtBeginEnd();
1018 void removeDoublePointsWholeTrack()
1020 mpBufferedData.reset();
1022 if(mpControlVector)
1024 sal_uInt32 nIndex(0);
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))
1030 bool bRemove(maPoints.getCoordinate(nIndex) == maPoints.getCoordinate(nIndex + 1));
1032 if(bRemove && mpControlVector)
1034 if(!mpControlVector->getNextVector(nIndex).equalZero() || !mpControlVector->getPrevVector(nIndex + 1).equalZero())
1036 bRemove = false;
1040 if(bRemove)
1042 if(mpControlVector && !mpControlVector->getPrevVector(nIndex).equalZero())
1044 mpControlVector->setPrevVector(nIndex + 1, mpControlVector->getPrevVector(nIndex));
1047 // if next is same as index and the control vectors are unused, delete index
1048 remove(nIndex, 1);
1050 else
1052 // if different, step forward
1053 nIndex++;
1057 else
1059 maPoints.removeDoublePointsWholeTrack();
1063 void transform(const basegfx::B2DHomMatrix& rMatrix)
1065 mpBufferedData.reset();
1067 if(mpControlVector)
1069 for(sal_uInt32 a(0); a < maPoints.count(); a++)
1071 basegfx::B2DPoint aCandidate = maPoints.getCoordinate(a);
1073 if(mpControlVector->isUsed())
1075 const basegfx::B2DVector& rPrevVector(mpControlVector->getPrevVector(a));
1076 const basegfx::B2DVector& rNextVector(mpControlVector->getNextVector(a));
1078 if(!rPrevVector.equalZero())
1080 basegfx::B2DVector aPrevVector(rMatrix * rPrevVector);
1081 mpControlVector->setPrevVector(a, aPrevVector);
1084 if(!rNextVector.equalZero())
1086 basegfx::B2DVector aNextVector(rMatrix * rNextVector);
1087 mpControlVector->setNextVector(a, aNextVector);
1091 aCandidate *= rMatrix;
1092 maPoints.setCoordinate(a, aCandidate);
1095 if(!mpControlVector->isUsed())
1096 mpControlVector.reset();
1098 else
1100 maPoints.transform(rMatrix);
1104 void addOrReplaceSystemDependentData(basegfx::SystemDependentData_SharedPtr& rData)
1106 if(!mpBufferedData)
1108 mpBufferedData.reset(new ImplBufferedData);
1111 mpBufferedData->addOrReplaceSystemDependentData(rData);
1114 basegfx::SystemDependentData_SharedPtr getSystemDependentData(size_t hash_code) const
1116 if(mpBufferedData)
1118 return mpBufferedData->getSystemDependentData(hash_code);
1121 return basegfx::SystemDependentData_SharedPtr();
1125 namespace basegfx
1127 B2DPolygon::B2DPolygon() = default;
1129 B2DPolygon::B2DPolygon(std::initializer_list<basegfx::B2DPoint> aPoints)
1130 : mpPolygon()
1132 for (const basegfx::B2DPoint& rPoint : aPoints)
1134 append(rPoint);
1138 B2DPolygon::B2DPolygon(const B2DPolygon&) = default;
1140 B2DPolygon::B2DPolygon(B2DPolygon&&) = default;
1142 B2DPolygon::B2DPolygon(const B2DPolygon& rPolygon, sal_uInt32 nIndex, sal_uInt32 nCount)
1143 : mpPolygon(ImplB2DPolygon(*rPolygon.mpPolygon, nIndex, nCount))
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 (!)");
1150 B2DPolygon::~B2DPolygon() = default;
1152 B2DPolygon& B2DPolygon::operator=(const B2DPolygon&) = default;
1154 B2DPolygon& B2DPolygon::operator=(B2DPolygon&&) = default;
1156 void B2DPolygon::makeUnique()
1158 mpPolygon.make_unique();
1161 bool B2DPolygon::operator==(const B2DPolygon& rPolygon) const
1163 if(mpPolygon.same_object(rPolygon.mpPolygon))
1164 return true;
1166 return ((*mpPolygon) == (*rPolygon.mpPolygon));
1169 bool B2DPolygon::operator!=(const B2DPolygon& rPolygon) const
1171 return !(*this == rPolygon);
1174 sal_uInt32 B2DPolygon::count() const
1176 return mpPolygon->count();
1179 B2DPoint const & B2DPolygon::getB2DPoint(sal_uInt32 nIndex) const
1181 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1183 return mpPolygon->getPoint(nIndex);
1186 void B2DPolygon::setB2DPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
1188 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1190 if(mpPolygon->getPoint(nIndex) != rValue)
1192 mpPolygon->setPoint(nIndex, rValue);
1196 void B2DPolygon::reserve(sal_uInt32 nCount)
1198 mpPolygon->reserve(nCount);
1201 void B2DPolygon::insert(sal_uInt32 nIndex, const B2DPoint& rPoint, sal_uInt32 nCount)
1203 OSL_ENSURE(nIndex <= mpPolygon->count(), "B2DPolygon Insert outside range (!)");
1205 if(nCount)
1207 mpPolygon->insert(nIndex, rPoint, nCount);
1211 void B2DPolygon::append(const B2DPoint& rPoint, sal_uInt32 nCount)
1213 if(nCount)
1215 mpPolygon->insert(mpPolygon->count(), rPoint, nCount);
1219 void B2DPolygon::append(const B2DPoint& rPoint)
1221 mpPolygon->append(rPoint);
1224 B2DPoint B2DPolygon::getPrevControlPoint(sal_uInt32 nIndex) const
1226 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1228 if(mpPolygon->areControlPointsUsed())
1230 return mpPolygon->getPoint(nIndex) + mpPolygon->getPrevControlVector(nIndex);
1232 else
1234 return mpPolygon->getPoint(nIndex);
1238 B2DPoint B2DPolygon::getNextControlPoint(sal_uInt32 nIndex) const
1240 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1242 if(mpPolygon->areControlPointsUsed())
1244 return mpPolygon->getPoint(nIndex) + mpPolygon->getNextControlVector(nIndex);
1246 else
1248 return mpPolygon->getPoint(nIndex);
1252 void B2DPolygon::setPrevControlPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
1254 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1255 const basegfx::B2DVector aNewVector(rValue - mpPolygon->getPoint(nIndex));
1257 if(mpPolygon->getPrevControlVector(nIndex) != aNewVector)
1259 mpPolygon->setPrevControlVector(nIndex, aNewVector);
1263 void B2DPolygon::setNextControlPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
1265 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1266 const basegfx::B2DVector aNewVector(rValue - mpPolygon->getPoint(nIndex));
1268 if(mpPolygon->getNextControlVector(nIndex) != aNewVector)
1270 mpPolygon->setNextControlVector(nIndex, aNewVector);
1274 void B2DPolygon::setControlPoints(sal_uInt32 nIndex, const basegfx::B2DPoint& rPrev, const basegfx::B2DPoint& rNext)
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);
1281 if(mpPolygon->getPrevControlVector(nIndex) != aNewPrev || mpPolygon->getNextControlVector(nIndex) != aNewNext)
1283 mpPolygon->setControlVectors(nIndex, aNewPrev, aNewNext);
1287 void B2DPolygon::resetPrevControlPoint(sal_uInt32 nIndex)
1289 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1291 if(mpPolygon->areControlPointsUsed() && !mpPolygon->getPrevControlVector(nIndex).equalZero())
1293 mpPolygon->setPrevControlVector(nIndex, B2DVector::getEmptyVector());
1297 void B2DPolygon::resetNextControlPoint(sal_uInt32 nIndex)
1299 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1301 if(mpPolygon->areControlPointsUsed() && !mpPolygon->getNextControlVector(nIndex).equalZero())
1303 mpPolygon->setNextControlVector(nIndex, B2DVector::getEmptyVector());
1307 void B2DPolygon::resetControlPoints()
1309 if(mpPolygon->areControlPointsUsed())
1311 mpPolygon->resetControlVectors();
1315 void B2DPolygon::appendBezierSegment(
1316 const B2DPoint& rNextControlPoint,
1317 const B2DPoint& rPrevControlPoint,
1318 const B2DPoint& rPoint)
1320 const B2DVector aNewNextVector(mpPolygon->count() ? B2DVector(rNextControlPoint - mpPolygon->getPoint(mpPolygon->count() - 1)) : B2DVector::getEmptyVector());
1321 const B2DVector aNewPrevVector(rPrevControlPoint - rPoint);
1323 if(aNewNextVector.equalZero() && aNewPrevVector.equalZero())
1325 mpPolygon->insert(mpPolygon->count(), rPoint, 1);
1327 else
1329 mpPolygon->appendBezierSegment(aNewNextVector, aNewPrevVector, rPoint);
1333 void B2DPolygon::appendQuadraticBezierSegment(const B2DPoint& rControlPoint, const B2DPoint& rPoint)
1335 if (mpPolygon->count() == 0)
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));
1342 else
1344 const B2DPoint aPreviousPoint(mpPolygon->getPoint(mpPolygon->count() - 1));
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);
1351 appendBezierSegment(B2DPoint(nX1, nY1), B2DPoint(nX2, nY2), rPoint);
1355 bool B2DPolygon::areControlPointsUsed() const
1357 return mpPolygon->areControlPointsUsed();
1360 bool B2DPolygon::isPrevControlPointUsed(sal_uInt32 nIndex) const
1362 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1364 return (mpPolygon->areControlPointsUsed() && !mpPolygon->getPrevControlVector(nIndex).equalZero());
1367 bool B2DPolygon::isNextControlPointUsed(sal_uInt32 nIndex) const
1369 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1371 return (mpPolygon->areControlPointsUsed() && !mpPolygon->getNextControlVector(nIndex).equalZero());
1374 B2VectorContinuity B2DPolygon::getContinuityInPoint(sal_uInt32 nIndex) const
1376 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1378 if(mpPolygon->areControlPointsUsed())
1380 const B2DVector& rPrev(mpPolygon->getPrevControlVector(nIndex));
1381 const B2DVector& rNext(mpPolygon->getNextControlVector(nIndex));
1383 return getContinuity(rPrev, rNext);
1385 else
1387 return B2VectorContinuity::NONE;
1391 void B2DPolygon::getBezierSegment(sal_uInt32 nIndex, B2DCubicBezier& rTarget) const
1393 OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
1394 const bool bNextIndexValidWithoutClose(nIndex + 1 < mpPolygon->count());
1396 if(bNextIndexValidWithoutClose || mpPolygon->isClosed())
1398 const sal_uInt32 nNextIndex(bNextIndexValidWithoutClose ? nIndex + 1 : 0);
1399 rTarget.setStartPoint(mpPolygon->getPoint(nIndex));
1400 rTarget.setEndPoint(mpPolygon->getPoint(nNextIndex));
1402 if(mpPolygon->areControlPointsUsed())
1404 rTarget.setControlPointA(rTarget.getStartPoint() + mpPolygon->getNextControlVector(nIndex));
1405 rTarget.setControlPointB(rTarget.getEndPoint() + mpPolygon->getPrevControlVector(nNextIndex));
1407 else
1409 // no bezier, reset control points at rTarget
1410 rTarget.setControlPointA(rTarget.getStartPoint());
1411 rTarget.setControlPointB(rTarget.getEndPoint());
1414 else
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);
1425 B2DPolygon const & B2DPolygon::getDefaultAdaptiveSubdivision() const
1427 return mpPolygon->getDefaultAdaptiveSubdivision(*this);
1430 B2DRange const & B2DPolygon::getB2DRange() const
1432 return mpPolygon->getB2DRange(*this);
1435 void B2DPolygon::append(const B2DPolygon& rPoly, sal_uInt32 nIndex, sal_uInt32 nCount)
1437 if(rPoly.count())
1439 if(!nCount)
1441 nCount = rPoly.count();
1444 if(nIndex == 0 && nCount == rPoly.count())
1446 mpPolygon->insert(mpPolygon->count(), *rPoly.mpPolygon);
1448 else
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);
1457 void B2DPolygon::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
1459 OSL_ENSURE(nIndex + nCount <= mpPolygon->count(), "B2DPolygon Remove outside range (!)");
1461 if(nCount)
1463 mpPolygon->remove(nIndex, nCount);
1467 void B2DPolygon::clear()
1469 *mpPolygon = ImplB2DPolygon();
1472 bool B2DPolygon::isClosed() const
1474 return mpPolygon->isClosed();
1477 void B2DPolygon::setClosed(bool bNew)
1479 if(isClosed() != bNew)
1481 mpPolygon->setClosed(bNew);
1485 void B2DPolygon::flip()
1487 if(count() > 1)
1489 mpPolygon->flip();
1493 bool B2DPolygon::hasDoublePoints() const
1495 return (mpPolygon->count() > 1 && mpPolygon->hasDoublePoints());
1498 void B2DPolygon::removeDoublePoints()
1500 if(hasDoublePoints())
1502 mpPolygon->removeDoublePointsAtBeginEnd();
1503 mpPolygon->removeDoublePointsWholeTrack();
1507 void B2DPolygon::transform(const B2DHomMatrix& rMatrix)
1509 if(mpPolygon->count() && !rMatrix.isIdentity())
1511 mpPolygon->transform(rMatrix);
1515 void B2DPolygon::addOrReplaceSystemDependentDataInternal(SystemDependentData_SharedPtr& rData) const
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());
1524 const_cast<ImplB2DPolygon*>(pMyImpl)->addOrReplaceSystemDependentData(rData);
1527 SystemDependentData_SharedPtr B2DPolygon::getSystemDependantDataInternal(size_t hash_code) const
1529 return mpPolygon->getSystemDependentData(hash_code);
1532 } // end of namespace basegfx
1534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */