Adjust includes
[LibreOffice.git] / svx / source / xoutdev / _xpoly.cxx
blob2fdc6f028ab599ad214bc26ca5b5aa36fe149228
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/endian.h>
21 #include <tools/stream.hxx>
22 #include <tools/debug.hxx>
23 #include <tools/poly.hxx>
24 #include <tools/helpers.hxx>
25 #include <tools/gen.hxx>
27 #include <svx/xpoly.hxx>
28 #include <xpolyimp.hxx>
29 #include <basegfx/polygon/b2dpolygon.hxx>
30 #include <basegfx/point/b2dpoint.hxx>
31 #include <basegfx/vector/b2dvector.hxx>
32 #include <basegfx/polygon/b2dpolygontools.hxx>
33 #include <basegfx/range/b2drange.hxx>
34 #include <basegfx/numeric/ftools.hxx>
37 ImpXPolygon::ImpXPolygon(sal_uInt16 nInitSize, sal_uInt16 _nResize)
38 : pPointAry(nullptr)
39 , pFlagAry(nullptr)
40 , pOldPointAry(nullptr)
41 , bDeleteOldPoints(false)
42 , nSize(0)
43 , nResize(_nResize)
44 , nPoints(0)
46 Resize(nInitSize);
49 ImpXPolygon::ImpXPolygon( const ImpXPolygon& rImpXPoly )
50 : pPointAry(nullptr)
51 , pFlagAry(nullptr)
52 , pOldPointAry(nullptr)
53 , bDeleteOldPoints(false)
54 , nSize(0)
55 , nResize(rImpXPoly.nResize)
56 , nPoints(0)
58 rImpXPoly.CheckPointDelete();
60 Resize( rImpXPoly.nSize );
62 // copy
63 nPoints = rImpXPoly.nPoints;
64 memcpy( pPointAry, rImpXPoly.pPointAry, nSize*sizeof( Point ) );
65 memcpy( pFlagAry.get(), rImpXPoly.pFlagAry.get(), nSize );
68 ImpXPolygon::~ImpXPolygon()
70 delete[] reinterpret_cast<char*>(pPointAry);
71 if ( bDeleteOldPoints )
73 delete[] reinterpret_cast<char*>(pOldPointAry);
74 pOldPointAry = nullptr;
78 bool ImpXPolygon::operator==(const ImpXPolygon& rImpXPoly) const
80 return nPoints==rImpXPoly.nPoints &&
81 (nPoints==0 ||
82 (memcmp(pPointAry, rImpXPoly.pPointAry, nPoints*sizeof(Point))==0 &&
83 memcmp(pFlagAry.get(), rImpXPoly.pFlagAry.get(), nPoints)==0));
86 /** Change polygon size
88 * @param nNewSize the new size of the polygon
89 * @param bDeletePoints if FALSE, do not delete the point array directly but
90 * wait for the next call before doing so. This prevents
91 * errors with XPoly[n] = XPoly[0] where a resize might
92 * destroy the right side point array too early.
94 void ImpXPolygon::Resize( sal_uInt16 nNewSize, bool bDeletePoints )
96 if( nNewSize == nSize )
97 return;
99 PolyFlags* pOldFlagAry = pFlagAry.release();
100 sal_uInt16 nOldSize = nSize;
102 CheckPointDelete();
103 pOldPointAry = pPointAry;
105 // Round the new size to a multiple of nResize, if
106 // the object was not newly created (nSize != 0)
107 if ( nSize != 0 && nNewSize > nSize )
109 DBG_ASSERT(nResize, "Trying to resize but nResize = 0 !");
110 nNewSize = nSize + ((nNewSize-nSize-1) / nResize + 1) * nResize;
112 // create point array
113 nSize = nNewSize;
114 pPointAry = reinterpret_cast<Point*>(new char[ nSize*sizeof( Point ) ]);
115 memset( pPointAry, 0, nSize*sizeof( Point ) );
117 // create flag array
118 pFlagAry.reset( new PolyFlags[ nSize ] );
119 memset( pFlagAry.get(), 0, nSize );
121 // copy if needed
122 if( nOldSize )
124 if( nOldSize < nSize )
126 memcpy( pPointAry, pOldPointAry, nOldSize*sizeof( Point ) );
127 memcpy( pFlagAry.get(), pOldFlagAry, nOldSize );
129 else
131 memcpy( pPointAry, pOldPointAry, nSize*sizeof( Point ) );
132 memcpy( pFlagAry.get(), pOldFlagAry, nSize );
134 // adjust number of valid points
135 if( nPoints > nSize )
136 nPoints = nSize;
138 if ( bDeletePoints )
140 delete[] reinterpret_cast<char*>(pOldPointAry);
141 pOldPointAry = nullptr;
143 else
144 bDeleteOldPoints = true;
145 delete[] pOldFlagAry;
149 void ImpXPolygon::InsertSpace( sal_uInt16 nPos, sal_uInt16 nCount )
151 CheckPointDelete();
153 if ( nPos > nPoints )
154 nPos = nPoints;
156 // if the polygon is too small than enlarge it
157 if( (nPoints + nCount) > nSize )
158 Resize( nPoints + nCount );
160 // If the insert is not at the last position, move everything after backwards
161 if( nPos < nPoints )
163 sal_uInt16 nMove = nPoints - nPos;
164 memmove( &pPointAry[nPos+nCount], &pPointAry[nPos],
165 nMove * sizeof(Point) );
166 memmove( &pFlagAry[nPos+nCount], &pFlagAry[nPos], nMove );
168 memset( &pPointAry[nPos], 0, nCount * sizeof( Point ) );
169 memset( &pFlagAry [nPos], 0, nCount );
171 nPoints = nPoints + nCount;
174 void ImpXPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
176 CheckPointDelete();
178 if( (nPos + nCount) <= nPoints )
180 sal_uInt16 nMove = nPoints - nPos - nCount;
182 if( nMove )
184 memmove( &pPointAry[nPos], &pPointAry[nPos+nCount],
185 nMove * sizeof(Point) );
186 memmove( &pFlagAry[nPos], &pFlagAry[nPos+nCount], nMove );
188 memset( &pPointAry[nPoints - nCount], 0, nCount * sizeof( Point ) );
189 memset( &pFlagAry [nPoints - nCount], 0, nCount );
190 nPoints = nPoints - nCount;
194 void ImpXPolygon::CheckPointDelete() const
196 if ( bDeleteOldPoints )
198 delete[] reinterpret_cast<char*>(pOldPointAry);
199 const_cast< ImpXPolygon* >(this)->pOldPointAry = nullptr;
200 const_cast< ImpXPolygon* >(this)->bDeleteOldPoints = false;
204 XPolygon::XPolygon( sal_uInt16 nSize )
205 : pImpXPolygon( ImpXPolygon( nSize, 16 ) )
209 XPolygon::XPolygon( const XPolygon& rXPoly )
210 : pImpXPolygon(rXPoly.pImpXPolygon)
214 XPolygon::XPolygon( XPolygon&& rXPoly )
215 : pImpXPolygon(std::move(rXPoly.pImpXPolygon))
219 /// create a XPolygon out of a standard polygon
220 XPolygon::XPolygon( const tools::Polygon& rPoly )
221 : pImpXPolygon( rPoly.GetSize() )
223 sal_uInt16 nSize = rPoly.GetSize();
224 pImpXPolygon->nPoints = nSize;
226 for( sal_uInt16 i = 0; i < nSize; i++ )
228 pImpXPolygon->pPointAry[i] = rPoly[i];
229 pImpXPolygon->pFlagAry[i] = rPoly.GetFlags( i );
233 /// create a rectangle (also with rounded corners) as a Bézier polygon
234 XPolygon::XPolygon(const tools::Rectangle& rRect, long nRx, long nRy)
235 : pImpXPolygon( 17 )
237 long nWh = (rRect.GetWidth() - 1) / 2;
238 long nHh = (rRect.GetHeight() - 1) / 2;
240 if ( nRx > nWh ) nRx = nWh;
241 if ( nRy > nHh ) nRy = nHh;
243 // negate Rx => circle clockwise
244 nRx = -nRx;
246 // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
247 long nXHdl = (long)(0.552284749 * nRx);
248 long nYHdl = (long)(0.552284749 * nRy);
249 sal_uInt16 nPos = 0;
251 if ( nRx && nRy )
253 Point aCenter;
255 for (sal_uInt16 nQuad = 0; nQuad < 4; nQuad++)
257 switch ( nQuad )
259 case 0: aCenter = rRect.TopLeft();
260 aCenter.X() -= nRx;
261 aCenter.Y() += nRy;
262 break;
263 case 1: aCenter = rRect.TopRight();
264 aCenter.X() += nRx;
265 aCenter.Y() += nRy;
266 break;
267 case 2: aCenter = rRect.BottomRight();
268 aCenter.X() += nRx;
269 aCenter.Y() -= nRy;
270 break;
271 case 3: aCenter = rRect.BottomLeft();
272 aCenter.X() -= nRx;
273 aCenter.Y() -= nRy;
274 break;
276 GenBezArc(aCenter, nRx, nRy, nXHdl, nYHdl, 0, 900, nQuad, nPos);
277 pImpXPolygon->pFlagAry[nPos ] = PolyFlags::Smooth;
278 pImpXPolygon->pFlagAry[nPos+3] = PolyFlags::Smooth;
279 nPos += 4;
282 else
284 pImpXPolygon->pPointAry[nPos++] = rRect.TopLeft();
285 pImpXPolygon->pPointAry[nPos++] = rRect.TopRight();
286 pImpXPolygon->pPointAry[nPos++] = rRect.BottomRight();
287 pImpXPolygon->pPointAry[nPos++] = rRect.BottomLeft();
289 pImpXPolygon->pPointAry[nPos] = pImpXPolygon->pPointAry[0];
290 pImpXPolygon->nPoints = nPos + 1;
293 /// create a ellipse (curve) as Bézier polygon
294 XPolygon::XPolygon(const Point& rCenter, long nRx, long nRy,
295 sal_uInt16 nStartAngle, sal_uInt16 nEndAngle, bool bClose)
296 : pImpXPolygon( 17 )
298 nStartAngle %= 3600;
299 if ( nEndAngle > 3600 ) nEndAngle %= 3600;
300 bool bFull = (nStartAngle == 0 && nEndAngle == 3600);
302 // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
303 long nXHdl = (long)(0.552284749 * nRx);
304 long nYHdl = (long)(0.552284749 * nRy);
305 sal_uInt16 nPos = 0;
306 bool bLoopEnd = false;
310 sal_uInt16 nA1, nA2;
311 sal_uInt16 nQuad = nStartAngle / 900;
312 if ( nQuad == 4 ) nQuad = 0;
313 bLoopEnd = CheckAngles(nStartAngle, nEndAngle, nA1, nA2);
314 GenBezArc(rCenter, nRx, nRy, nXHdl, nYHdl, nA1, nA2, nQuad, nPos);
315 nPos += 3;
316 if ( !bLoopEnd )
317 pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
319 } while ( !bLoopEnd );
321 // if not a full circle than connect edges with center point if necessary
322 if ( !bFull && bClose )
323 pImpXPolygon->pPointAry[++nPos] = rCenter;
325 if ( bFull )
327 pImpXPolygon->pFlagAry[0 ] = PolyFlags::Smooth;
328 pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
330 pImpXPolygon->nPoints = nPos + 1;
333 XPolygon::~XPolygon()
337 void XPolygon::SetPointCount( sal_uInt16 nPoints )
339 pImpXPolygon->CheckPointDelete();
341 if( pImpXPolygon->nSize < nPoints )
342 pImpXPolygon->Resize( nPoints );
344 if ( nPoints < pImpXPolygon->nPoints )
346 sal_uInt16 nSize = pImpXPolygon->nPoints - nPoints;
347 memset( &pImpXPolygon->pPointAry[nPoints], 0, nSize * sizeof( Point ) );
348 memset( &pImpXPolygon->pFlagAry [nPoints], 0, nSize );
350 pImpXPolygon->nPoints = nPoints;
353 sal_uInt16 XPolygon::GetSize() const
355 pImpXPolygon->CheckPointDelete();
356 return pImpXPolygon->nSize;
359 sal_uInt16 XPolygon::GetPointCount() const
361 pImpXPolygon->CheckPointDelete();
362 return pImpXPolygon->nPoints;
365 void XPolygon::Insert( sal_uInt16 nPos, const Point& rPt, PolyFlags eFlags )
367 if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
368 pImpXPolygon->InsertSpace( nPos, 1 );
369 pImpXPolygon->pPointAry[nPos] = rPt;
370 pImpXPolygon->pFlagAry[nPos] = eFlags;
373 void XPolygon::Insert( sal_uInt16 nPos, const XPolygon& rXPoly )
375 if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
377 sal_uInt16 nPoints = rXPoly.GetPointCount();
379 pImpXPolygon->InsertSpace( nPos, nPoints );
381 memcpy( &(pImpXPolygon->pPointAry[nPos]),
382 rXPoly.pImpXPolygon->pPointAry,
383 nPoints*sizeof( Point ) );
384 memcpy( &(pImpXPolygon->pFlagAry[nPos]),
385 rXPoly.pImpXPolygon->pFlagAry.get(),
386 nPoints );
389 void XPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
391 pImpXPolygon->Remove( nPos, nCount );
394 void XPolygon::Move( long nHorzMove, long nVertMove )
396 if ( !nHorzMove && !nVertMove )
397 return;
399 // move points
400 sal_uInt16 nCount = pImpXPolygon->nPoints;
401 for ( sal_uInt16 i = 0; i < nCount; i++ )
403 Point* pPt = &(pImpXPolygon->pPointAry[i]);
404 pPt->X() += nHorzMove;
405 pPt->Y() += nVertMove;
409 tools::Rectangle XPolygon::GetBoundRect() const
411 pImpXPolygon->CheckPointDelete();
412 tools::Rectangle aRetval;
414 if(pImpXPolygon->nPoints)
416 // #i37709#
417 // For historical reasons the control points are not part of the
418 // BoundRect. This makes it necessary to subdivide the polygon to
419 // get a relatively correct BoundRect. Numerically, this is not
420 // correct and never was.
422 const basegfx::B2DRange aPolygonRange(basegfx::utils::getRange(getB2DPolygon()));
423 aRetval = tools::Rectangle(
424 FRound(aPolygonRange.getMinX()), FRound(aPolygonRange.getMinY()),
425 FRound(aPolygonRange.getMaxX()), FRound(aPolygonRange.getMaxY()));
428 return aRetval;
431 const Point& XPolygon::operator[]( sal_uInt16 nPos ) const
433 DBG_ASSERT(nPos < pImpXPolygon->nPoints, "Invalid index at const array access to XPolygon");
435 pImpXPolygon->CheckPointDelete();
436 return pImpXPolygon->pPointAry[nPos];
439 Point& XPolygon::operator[]( sal_uInt16 nPos )
441 pImpXPolygon->CheckPointDelete();
443 if( nPos >= pImpXPolygon->nSize )
445 DBG_ASSERT(pImpXPolygon->nResize, "Invalid index at array access to XPolygon");
446 pImpXPolygon->Resize(nPos + 1, false);
448 if( nPos >= pImpXPolygon->nPoints )
449 pImpXPolygon->nPoints = nPos + 1;
451 return pImpXPolygon->pPointAry[nPos];
454 XPolygon& XPolygon::operator=( const XPolygon& rXPoly )
456 pImpXPolygon = rXPoly.pImpXPolygon;
457 return *this;
460 XPolygon& XPolygon::operator=( XPolygon&& rXPoly )
462 pImpXPolygon = std::move(rXPoly.pImpXPolygon);
463 return *this;
466 bool XPolygon::operator==( const XPolygon& rXPoly ) const
468 pImpXPolygon->CheckPointDelete();
469 return rXPoly.pImpXPolygon == pImpXPolygon;
472 /// get the flags for the point at the given position
473 PolyFlags XPolygon::GetFlags( sal_uInt16 nPos ) const
475 pImpXPolygon->CheckPointDelete();
476 return pImpXPolygon->pFlagAry[nPos];
479 /// set the flags for the point at the given position
480 void XPolygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags )
482 pImpXPolygon->CheckPointDelete();
483 pImpXPolygon->pFlagAry[nPos] = eFlags;
486 /// short path to read the CONTROL flag directly (TODO: better explain what the sense behind this flag is!)
487 bool XPolygon::IsControl(sal_uInt16 nPos) const
489 return pImpXPolygon->pFlagAry[nPos] == PolyFlags::Control;
492 /// short path to read the SMOOTH and SYMMTR flag directly (TODO: better explain what the sense behind these flags is!)
493 bool XPolygon::IsSmooth(sal_uInt16 nPos) const
495 PolyFlags eFlag = pImpXPolygon->pFlagAry[nPos];
496 return ( eFlag == PolyFlags::Smooth || eFlag == PolyFlags::Symmetric );
499 /** calculate the euclidean distance between two points
501 * @param nP1 The first point
502 * @param nP2 The second point
504 double XPolygon::CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
506 const Point& rP1 = pImpXPolygon->pPointAry[nP1];
507 const Point& rP2 = pImpXPolygon->pPointAry[nP2];
508 double fDx = rP2.X() - rP1.X();
509 double fDy = rP2.Y() - rP1.Y();
510 return sqrt(fDx * fDx + fDy * fDy);
513 void XPolygon::SubdivideBezier(sal_uInt16 nPos, bool bCalcFirst, double fT)
515 Point* pPoints = pImpXPolygon->pPointAry;
516 double fT2 = fT * fT;
517 double fT3 = fT * fT2;
518 double fU = 1.0 - fT;
519 double fU2 = fU * fU;
520 double fU3 = fU * fU2;
521 sal_uInt16 nIdx = nPos;
522 short nPosInc, nIdxInc;
524 if ( bCalcFirst )
526 nPos += 3;
527 nPosInc = -1;
528 nIdxInc = 0;
530 else
532 nPosInc = 1;
533 nIdxInc = 1;
535 pPoints[nPos].X() = (long) (fU3 * pPoints[nIdx ].X() +
536 fT * fU2 * pPoints[nIdx+1].X() * 3 +
537 fT2 * fU * pPoints[nIdx+2].X() * 3 +
538 fT3 * pPoints[nIdx+3].X());
539 pPoints[nPos].Y() = (long) (fU3 * pPoints[nIdx ].Y() +
540 fT * fU2 * pPoints[nIdx+1].Y() * 3 +
541 fT2 * fU * pPoints[nIdx+2].Y() * 3 +
542 fT3 * pPoints[nIdx+3].Y());
543 nPos = nPos + nPosInc;
544 nIdx = nIdx + nIdxInc;
545 pPoints[nPos].X() = (long) (fU2 * pPoints[nIdx ].X() +
546 fT * fU * pPoints[nIdx+1].X() * 2 +
547 fT2 * pPoints[nIdx+2].X());
548 pPoints[nPos].Y() = (long) (fU2 * pPoints[nIdx ].Y() +
549 fT * fU * pPoints[nIdx+1].Y() * 2 +
550 fT2 * pPoints[nIdx+2].Y());
551 nPos = nPos + nPosInc;
552 nIdx = nIdx + nIdxInc;
553 pPoints[nPos].X() = (long) (fU * pPoints[nIdx ].X() +
554 fT * pPoints[nIdx+1].X());
555 pPoints[nPos].Y() = (long) (fU * pPoints[nIdx ].Y() +
556 fT * pPoints[nIdx+1].Y());
559 /// Generate a Bézier arc
560 void XPolygon::GenBezArc(const Point& rCenter, long nRx, long nRy,
561 long nXHdl, long nYHdl, sal_uInt16 nStart, sal_uInt16 nEnd,
562 sal_uInt16 nQuad, sal_uInt16 nFirst)
564 Point* pPoints = pImpXPolygon->pPointAry;
565 pPoints[nFirst ] = rCenter;
566 pPoints[nFirst+3] = rCenter;
568 if ( nQuad == 1 || nQuad == 2 )
570 nRx = -nRx; nXHdl = -nXHdl;
572 if ( nQuad == 0 || nQuad == 1 )
574 nRy = -nRy; nYHdl = -nYHdl;
577 if ( nQuad == 0 || nQuad == 2 )
579 pPoints[nFirst].X() += nRx; pPoints[nFirst+3].Y() += nRy;
581 else
583 pPoints[nFirst].Y() += nRy; pPoints[nFirst+3].X() += nRx;
585 pPoints[nFirst+1] = pPoints[nFirst];
586 pPoints[nFirst+2] = pPoints[nFirst+3];
588 if ( nQuad == 0 || nQuad == 2 )
590 pPoints[nFirst+1].Y() += nYHdl; pPoints[nFirst+2].X() += nXHdl;
592 else
594 pPoints[nFirst+1].X() += nXHdl; pPoints[nFirst+2].Y() += nYHdl;
596 if ( nStart > 0 )
597 SubdivideBezier(nFirst, false, (double)nStart / 900);
598 if ( nEnd < 900 )
599 SubdivideBezier(nFirst, true, (double)(nEnd-nStart) / (900-nStart));
600 SetFlags(nFirst+1, PolyFlags::Control);
601 SetFlags(nFirst+2, PolyFlags::Control);
604 bool XPolygon::CheckAngles(sal_uInt16& nStart, sal_uInt16 nEnd, sal_uInt16& nA1, sal_uInt16& nA2)
606 if ( nStart == 3600 ) nStart = 0;
607 if ( nEnd == 0 ) nEnd = 3600;
608 sal_uInt16 nStPrev = nStart;
609 sal_uInt16 nMax = (nStart / 900 + 1) * 900;
610 sal_uInt16 nMin = nMax - 900;
612 if ( nEnd >= nMax || nEnd <= nStart ) nA2 = 900;
613 else nA2 = nEnd - nMin;
614 nA1 = nStart - nMin;
615 nStart = nMax;
617 // returns true when the last segment was calculated
618 return (nStPrev < nEnd && nStart >= nEnd);
621 /** Calculate a smooth transition to connect two Bézier curves
623 * This is done by projecting the corresponding point onto a line between
624 * two other points.
626 * @param nCenter The point at the end or beginning of the curve.
627 * If nCenter is at the end of the polygon the point is moved
628 * to the opposite side.
629 * @param nDrag The moved point that specifies the relocation.
630 * @param nPnt The point to modify.
632 void XPolygon::CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
634 // If nPoint is no control point, i.e. cannot be moved, than
635 // move nDrag instead on the line between nCenter and nPnt
636 if ( !IsControl(nPnt) )
638 sal_uInt16 nTmp = nDrag;
639 nDrag = nPnt;
640 nPnt = nTmp;
642 Point* pPoints = pImpXPolygon->pPointAry;
643 Point aDiff = pPoints[nDrag] - pPoints[nCenter];
644 double fDiv = CalcDistance(nCenter, nDrag);
646 if ( fDiv )
648 double fRatio = CalcDistance(nCenter, nPnt) / fDiv;
649 // keep the length if SMOOTH
650 if ( GetFlags(nCenter) == PolyFlags::Smooth || !IsControl(nDrag) )
652 aDiff.X() = (long) (fRatio * aDiff.X());
653 aDiff.Y() = (long) (fRatio * aDiff.Y());
655 pPoints[nPnt] = pPoints[nCenter] - aDiff;
659 /** Calculate tangent between two Bézier curves
661 * @param nCenter start or end point of the curves
662 * @param nPrev previous reference point
663 * @param nNext next reference point
665 void XPolygon::CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
667 double fAbsLen = CalcDistance(nNext, nPrev);
669 if ( !fAbsLen )
670 return;
672 const Point& rCenter = pImpXPolygon->pPointAry[nCenter];
673 Point& rNext = pImpXPolygon->pPointAry[nNext];
674 Point& rPrev = pImpXPolygon->pPointAry[nPrev];
675 Point aDiff = rNext - rPrev;
676 double fNextLen = CalcDistance(nCenter, nNext) / fAbsLen;
677 double fPrevLen = CalcDistance(nCenter, nPrev) / fAbsLen;
679 // same length for both sides if SYMMTR
680 if ( GetFlags(nCenter) == PolyFlags::Symmetric )
682 fPrevLen = (fNextLen + fPrevLen) / 2;
683 fNextLen = fPrevLen;
685 rNext.X() = rCenter.X() + (long) (fNextLen * aDiff.X());
686 rNext.Y() = rCenter.Y() + (long) (fNextLen * aDiff.Y());
687 rPrev.X() = rCenter.X() - (long) (fPrevLen * aDiff.X());
688 rPrev.Y() = rCenter.Y() - (long) (fPrevLen * aDiff.Y());
691 /// convert four polygon points into a Bézier curve
692 void XPolygon::PointsToBezier(sal_uInt16 nFirst)
694 double nFullLength, nPart1Length, nPart2Length;
695 double fX0, fY0, fX1, fY1, fX2, fY2, fX3, fY3;
696 double fTx1, fTx2, fTy1, fTy2;
697 double fT1, fU1, fT2, fU2, fV;
698 Point* pPoints = pImpXPolygon->pPointAry;
700 if ( nFirst > pImpXPolygon->nPoints - 4 || IsControl(nFirst) ||
701 IsControl(nFirst+1) || IsControl(nFirst+2) || IsControl(nFirst+3) )
702 return;
704 fTx1 = pPoints[nFirst+1].X();
705 fTy1 = pPoints[nFirst+1].Y();
706 fTx2 = pPoints[nFirst+2].X();
707 fTy2 = pPoints[nFirst+2].Y();
708 fX0 = pPoints[nFirst ].X();
709 fY0 = pPoints[nFirst ].Y();
710 fX3 = pPoints[nFirst+3].X();
711 fY3 = pPoints[nFirst+3].Y();
713 nPart1Length = CalcDistance(nFirst, nFirst+1);
714 nPart2Length = nPart1Length + CalcDistance(nFirst+1, nFirst+2);
715 nFullLength = nPart2Length + CalcDistance(nFirst+2, nFirst+3);
716 if ( nFullLength < 20 )
717 return;
719 if ( nPart2Length == nFullLength )
720 nPart2Length -= 1;
721 if ( nPart1Length == nFullLength )
722 nPart1Length = nPart2Length - 1;
723 if ( nPart1Length <= 0 )
724 nPart1Length = 1;
725 if ( nPart2Length <= 0 || nPart2Length == nPart1Length )
726 nPart2Length = nPart1Length + 1;
728 fT1 = nPart1Length / nFullLength;
729 fU1 = 1.0 - fT1;
730 fT2 = nPart2Length / nFullLength;
731 fU2 = 1.0 - fT2;
732 fV = 3 * (1.0 - (fT1 * fU2) / (fT2 * fU1));
734 fX1 = fTx1 / (fT1 * fU1 * fU1) - fTx2 * fT1 / (fT2 * fT2 * fU1 * fU2);
735 fX1 /= fV;
736 fX1 -= fX0 * ( fU1 / fT1 + fU2 / fT2) / 3;
737 fX1 += fX3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
739 fY1 = fTy1 / (fT1 * fU1 * fU1) - fTy2 * fT1 / (fT2 * fT2 * fU1 * fU2);
740 fY1 /= fV;
741 fY1 -= fY0 * ( fU1 / fT1 + fU2 / fT2) / 3;
742 fY1 += fY3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
744 fX2 = fTx2 / (fT2 * fT2 * fU2 * 3) - fX0 * fU2 * fU2 / ( fT2 * fT2 * 3);
745 fX2 -= fX1 * fU2 / fT2;
746 fX2 -= fX3 * fT2 / (fU2 * 3);
748 fY2 = fTy2 / (fT2 * fT2 * fU2 * 3) - fY0 * fU2 * fU2 / ( fT2 * fT2 * 3);
749 fY2 -= fY1 * fU2 / fT2;
750 fY2 -= fY3 * fT2 / (fU2 * 3);
752 pPoints[nFirst+1] = Point((long) fX1, (long) fY1);
753 pPoints[nFirst+2] = Point((long) fX2, (long) fY2);
754 SetFlags(nFirst+1, PolyFlags::Control);
755 SetFlags(nFirst+2, PolyFlags::Control);
758 /// scale in X- and/or Y-direction
759 void XPolygon::Scale(double fSx, double fSy)
761 pImpXPolygon->CheckPointDelete();
763 sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
765 for (sal_uInt16 i = 0; i < nPntCnt; i++)
767 Point& rPnt = pImpXPolygon->pPointAry[i];
768 rPnt.X() = (long)(fSx * rPnt.X());
769 rPnt.Y() = (long)(fSy * rPnt.Y());
774 * Distort a polygon by scaling its coordinates relative to a reference
775 * rectangle into an arbitrary rectangle.
777 * Mapping between polygon corners and reference rectangle:
778 * 0: top left 0----1
779 * 1: top right | |
780 * 2: bottom right 3----2
781 * 3: bottom left
783 void XPolygon::Distort(const tools::Rectangle& rRefRect,
784 const XPolygon& rDistortedRect)
786 pImpXPolygon->CheckPointDelete();
788 long Xr, Wr;
789 long Yr, Hr;
791 Xr = rRefRect.Left();
792 Yr = rRefRect.Top();
793 Wr = rRefRect.GetWidth();
794 Hr = rRefRect.GetHeight();
796 if ( !Wr || !Hr )
797 return;
799 long X1, X2, X3, X4;
800 long Y1, Y2, Y3, Y4;
801 DBG_ASSERT(rDistortedRect.pImpXPolygon->nPoints >= 4,
802 "Distort: rectangle to small");
804 X1 = rDistortedRect[0].X();
805 Y1 = rDistortedRect[0].Y();
806 X2 = rDistortedRect[1].X();
807 Y2 = rDistortedRect[1].Y();
808 X3 = rDistortedRect[3].X();
809 Y3 = rDistortedRect[3].Y();
810 X4 = rDistortedRect[2].X();
811 Y4 = rDistortedRect[2].Y();
813 sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
815 for (sal_uInt16 i = 0; i < nPntCnt; i++)
817 double fTx, fTy, fUx, fUy;
818 Point& rPnt = pImpXPolygon->pPointAry[i];
820 fTx = (double)(rPnt.X() - Xr) / Wr;
821 fTy = (double)(rPnt.Y() - Yr) / Hr;
822 fUx = 1.0 - fTx;
823 fUy = 1.0 - fTy;
825 rPnt.X() = (long) ( fUy * (fUx * X1 + fTx * X2) +
826 fTy * (fUx * X3 + fTx * X4) );
827 rPnt.Y() = (long) ( fUx * (fUy * Y1 + fTy * Y3) +
828 fTx * (fUy * Y2 + fTy * Y4) );
832 basegfx::B2DPolygon XPolygon::getB2DPolygon() const
834 // #i74631# use tools Polygon class for conversion to not have the code doubled
835 // here. This needs one more conversion but avoids different convertors in
836 // the long run
837 const tools::Polygon aSource(GetPointCount(), pImpXPolygon->pPointAry, pImpXPolygon->pFlagAry.get());
839 return aSource.getB2DPolygon();
842 XPolygon::XPolygon(const basegfx::B2DPolygon& rPolygon)
843 : pImpXPolygon( tools::Polygon( rPolygon ).GetSize() )
845 // #i74631# use tools Polygon class for conversion to not have the code doubled
846 // here. This needs one more conversion but avoids different convertors in
847 // the long run
849 const tools::Polygon aSource(rPolygon);
850 sal_uInt16 nSize = aSource.GetSize();
851 pImpXPolygon->nPoints = nSize;
853 for( sal_uInt16 i = 0; i < nSize; i++ )
855 pImpXPolygon->pPointAry[i] = aSource[i];
856 pImpXPolygon->pFlagAry[i] = aSource.GetFlags( i );
860 // XPolyPolygon
862 ImpXPolyPolygon::ImpXPolyPolygon( const ImpXPolyPolygon& rImpXPolyPoly )
863 : aXPolyList( rImpXPolyPoly.aXPolyList )
867 ImpXPolyPolygon::~ImpXPolyPolygon()
871 XPolyPolygon::XPolyPolygon()
872 : pImpXPolyPolygon()
876 XPolyPolygon::XPolyPolygon( const XPolyPolygon& rXPolyPoly )
877 : pImpXPolyPolygon( rXPolyPoly.pImpXPolyPolygon )
881 XPolyPolygon::XPolyPolygon( XPolyPolygon&& rXPolyPoly )
882 : pImpXPolyPolygon( std::move(rXPolyPoly.pImpXPolyPolygon) )
886 XPolyPolygon::XPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
887 : pImpXPolyPolygon()
889 for(sal_uInt32 a(0); a < rPolyPolygon.count(); a++)
891 const basegfx::B2DPolygon aCandidate = rPolyPolygon.getB2DPolygon(a);
892 Insert(XPolygon(aCandidate));
896 XPolyPolygon::~XPolyPolygon()
900 void XPolyPolygon::Insert( XPolygon&& rXPoly )
902 pImpXPolyPolygon->aXPolyList.emplace_back( std::move(rXPoly) );
905 /// insert all XPolygons of a XPolyPolygon
906 void XPolyPolygon::Insert( const XPolyPolygon& rXPolyPoly )
908 for ( size_t i = 0; i < rXPolyPoly.Count(); i++)
910 pImpXPolyPolygon->aXPolyList.emplace_back( rXPolyPoly[i] );
914 void XPolyPolygon::Remove( sal_uInt16 nPos )
916 pImpXPolyPolygon->aXPolyList.erase( pImpXPolyPolygon->aXPolyList.begin() + nPos );
919 const XPolygon& XPolyPolygon::GetObject( sal_uInt16 nPos ) const
921 return pImpXPolyPolygon->aXPolyList[ nPos ];
924 void XPolyPolygon::Clear()
926 pImpXPolyPolygon->aXPolyList.clear();
929 sal_uInt16 XPolyPolygon::Count() const
931 return (sal_uInt16)(pImpXPolyPolygon->aXPolyList.size());
934 tools::Rectangle XPolyPolygon::GetBoundRect() const
936 size_t nXPoly = pImpXPolyPolygon->aXPolyList.size();
937 tools::Rectangle aRect;
939 for ( size_t n = 0; n < nXPoly; n++ )
941 XPolygon const & rXPoly = pImpXPolyPolygon->aXPolyList[ n ];
942 aRect.Union( rXPoly.GetBoundRect() );
945 return aRect;
948 XPolygon& XPolyPolygon::operator[]( sal_uInt16 nPos )
950 return pImpXPolyPolygon->aXPolyList[ nPos ];
953 XPolyPolygon& XPolyPolygon::operator=( const XPolyPolygon& rXPolyPoly )
955 pImpXPolyPolygon = rXPolyPoly.pImpXPolyPolygon;
956 return *this;
959 XPolyPolygon& XPolyPolygon::operator=( XPolyPolygon&& rXPolyPoly )
961 pImpXPolyPolygon = std::move(rXPolyPoly.pImpXPolyPolygon);
962 return *this;
966 * Distort a polygon by scaling its coordinates relative to a reference
967 * rectangle into an arbitrary rectangle.
969 * Mapping between polygon corners and reference rectangle:
970 * 0: top left 0----1
971 * 1: top right | |
972 * 2: bottom right 3----2
973 * 3: bottom left
975 void XPolyPolygon::Distort(const tools::Rectangle& rRefRect,
976 const XPolygon& rDistortedRect)
978 for (size_t i = 0; i < Count(); i++)
979 pImpXPolyPolygon->aXPolyList[ i ].Distort(rRefRect, rDistortedRect);
982 basegfx::B2DPolyPolygon XPolyPolygon::getB2DPolyPolygon() const
984 basegfx::B2DPolyPolygon aRetval;
986 for(sal_uInt16 a(0); a < Count(); a++)
988 const XPolygon& rPoly = (*this)[a];
989 aRetval.append(rPoly.getB2DPolygon());
992 return aRetval;
995 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */