bump product version to 7.2.5.1
[LibreOffice.git] / svx / source / xoutdev / _xpoly.cxx
blob43c4dfb64605bef2f4409955f25b2dc963b798bf
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 <sal/config.h>
22 #include <algorithm>
24 #include <tools/debug.hxx>
25 #include <tools/poly.hxx>
26 #include <tools/helpers.hxx>
27 #include <tools/gen.hxx>
29 #include <svx/xpoly.hxx>
30 #include <xpolyimp.hxx>
31 #include <basegfx/polygon/b2dpolygon.hxx>
32 #include <basegfx/polygon/b2dpolygontools.hxx>
33 #include <basegfx/range/b2drange.hxx>
36 ImpXPolygon::ImpXPolygon(sal_uInt16 nInitSize, sal_uInt16 _nResize)
37 : pOldPointAry(nullptr)
38 , bDeleteOldPoints(false)
39 , nSize(0)
40 , nResize(_nResize)
41 , nPoints(0)
43 Resize(nInitSize);
46 ImpXPolygon::ImpXPolygon( const ImpXPolygon& rImpXPoly )
47 : pOldPointAry(nullptr)
48 , bDeleteOldPoints(false)
49 , nSize(0)
50 , nResize(rImpXPoly.nResize)
51 , nPoints(0)
53 rImpXPoly.CheckPointDelete();
55 Resize( rImpXPoly.nSize );
57 // copy
58 nPoints = rImpXPoly.nPoints;
59 memcpy( pPointAry.get(), rImpXPoly.pPointAry.get(), nSize*sizeof( Point ) );
60 memcpy( pFlagAry.get(), rImpXPoly.pFlagAry.get(), nSize );
63 ImpXPolygon::~ImpXPolygon()
65 pPointAry.reset();
66 if ( bDeleteOldPoints )
68 delete[] pOldPointAry;
69 pOldPointAry = nullptr;
73 bool ImpXPolygon::operator==(const ImpXPolygon& rImpXPoly) const
75 return nPoints==rImpXPoly.nPoints &&
76 (nPoints==0 ||
77 (memcmp(pPointAry.get(), rImpXPoly.pPointAry.get(), nPoints*sizeof(Point))==0 &&
78 memcmp(pFlagAry.get(), rImpXPoly.pFlagAry.get(), nPoints)==0));
81 /** Change polygon size
83 * @param nNewSize the new size of the polygon
84 * @param bDeletePoints if FALSE, do not delete the point array directly but
85 * wait for the next call before doing so. This prevents
86 * errors with XPoly[n] = XPoly[0] where a resize might
87 * destroy the right side point array too early.
89 void ImpXPolygon::Resize( sal_uInt16 nNewSize, bool bDeletePoints )
91 if( nNewSize == nSize )
92 return;
94 PolyFlags* pOldFlagAry = pFlagAry.release();
95 sal_uInt16 nOldSize = nSize;
97 CheckPointDelete();
98 pOldPointAry = pPointAry.release();
100 // Round the new size to a multiple of nResize, if
101 // the object was not newly created (nSize != 0)
102 if ( nSize != 0 && nNewSize > nSize )
104 DBG_ASSERT(nResize, "Trying to resize but nResize = 0 !");
105 nNewSize = nSize + ((nNewSize-nSize-1) / nResize + 1) * nResize;
107 // create point array
108 nSize = nNewSize;
109 pPointAry.reset( new Point[ nSize ] );
111 // create flag array
112 pFlagAry.reset( new PolyFlags[ nSize ] );
113 memset( pFlagAry.get(), 0, nSize );
115 // copy if needed
116 if( !nOldSize )
117 return;
119 if( nOldSize < nSize )
121 memcpy( pPointAry.get(), pOldPointAry, nOldSize*sizeof( Point ) );
122 memcpy( pFlagAry.get(), pOldFlagAry, nOldSize );
124 else
126 memcpy( pPointAry.get(), pOldPointAry, nSize*sizeof( Point ) );
127 memcpy( pFlagAry.get(), pOldFlagAry, nSize );
129 // adjust number of valid points
130 if( nPoints > nSize )
131 nPoints = nSize;
133 if ( bDeletePoints )
135 delete[] pOldPointAry;
136 pOldPointAry = nullptr;
138 else
139 bDeleteOldPoints = true;
140 delete[] pOldFlagAry;
143 void ImpXPolygon::InsertSpace( sal_uInt16 nPos, sal_uInt16 nCount )
145 CheckPointDelete();
147 if ( nPos > nPoints )
148 nPos = nPoints;
150 // if the polygon is too small then enlarge it
151 if( (nPoints + nCount) > nSize )
152 Resize( nPoints + nCount );
154 // If the insert is not at the last position, move everything after backwards
155 if( nPos < nPoints )
157 sal_uInt16 nMove = nPoints - nPos;
158 memmove( &pPointAry[nPos+nCount], &pPointAry[nPos],
159 nMove * sizeof(Point) );
160 memmove( &pFlagAry[nPos+nCount], &pFlagAry[nPos], nMove );
162 std::fill(pPointAry.get() + nPos, pPointAry.get() + nPos + nCount, Point());
163 memset( &pFlagAry [nPos], 0, nCount );
165 nPoints = nPoints + nCount;
168 void ImpXPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
170 CheckPointDelete();
172 if( (nPos + nCount) > nPoints )
173 return;
175 sal_uInt16 nMove = nPoints - nPos - nCount;
177 if( nMove )
179 memmove( &pPointAry[nPos], &pPointAry[nPos+nCount],
180 nMove * sizeof(Point) );
181 memmove( &pFlagAry[nPos], &pFlagAry[nPos+nCount], nMove );
183 std::fill(pPointAry.get() + (nPoints - nCount), pPointAry.get() + nPoints, Point());
184 memset( &pFlagAry [nPoints - nCount], 0, nCount );
185 nPoints = nPoints - nCount;
188 void ImpXPolygon::CheckPointDelete() const
190 if ( bDeleteOldPoints )
192 delete[] pOldPointAry;
193 const_cast< ImpXPolygon* >(this)->pOldPointAry = nullptr;
194 const_cast< ImpXPolygon* >(this)->bDeleteOldPoints = false;
198 XPolygon::XPolygon( sal_uInt16 nSize )
199 : pImpXPolygon( ImpXPolygon( nSize, 16 ) )
203 XPolygon::XPolygon( const XPolygon& ) = default;
205 XPolygon::XPolygon( XPolygon&& ) = default;
207 /// create a XPolygon out of a standard polygon
208 XPolygon::XPolygon( const tools::Polygon& rPoly )
209 : pImpXPolygon( rPoly.GetSize() )
211 sal_uInt16 nSize = rPoly.GetSize();
212 pImpXPolygon->nPoints = nSize;
214 for( sal_uInt16 i = 0; i < nSize; i++ )
216 pImpXPolygon->pPointAry[i] = rPoly[i];
217 pImpXPolygon->pFlagAry[i] = rPoly.GetFlags( i );
221 /// create a rectangle (also with rounded corners) as a Bézier polygon
222 XPolygon::XPolygon(const tools::Rectangle& rRect, tools::Long nRx, tools::Long nRy)
223 : pImpXPolygon( 17 )
225 tools::Long nWh = (rRect.GetWidth() - 1) / 2;
226 tools::Long nHh = (rRect.GetHeight() - 1) / 2;
228 if ( nRx > nWh ) nRx = nWh;
229 if ( nRy > nHh ) nRy = nHh;
231 // negate Rx => circle clockwise
232 nRx = -nRx;
234 // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
235 tools::Long nXHdl = static_cast<tools::Long>(0.552284749 * nRx);
236 tools::Long nYHdl = static_cast<tools::Long>(0.552284749 * nRy);
237 sal_uInt16 nPos = 0;
239 if ( nRx && nRy )
241 Point aCenter;
243 for (sal_uInt16 nQuad = 0; nQuad < 4; nQuad++)
245 switch ( nQuad )
247 case 0: aCenter = rRect.TopLeft();
248 aCenter.AdjustX( -nRx );
249 aCenter.AdjustY(nRy );
250 break;
251 case 1: aCenter = rRect.TopRight();
252 aCenter.AdjustX(nRx );
253 aCenter.AdjustY(nRy );
254 break;
255 case 2: aCenter = rRect.BottomRight();
256 aCenter.AdjustX(nRx );
257 aCenter.AdjustY( -nRy );
258 break;
259 case 3: aCenter = rRect.BottomLeft();
260 aCenter.AdjustX( -nRx );
261 aCenter.AdjustY( -nRy );
262 break;
264 GenBezArc(aCenter, nRx, nRy, nXHdl, nYHdl, 0_deg100, 9000_deg100, nQuad, nPos);
265 pImpXPolygon->pFlagAry[nPos ] = PolyFlags::Smooth;
266 pImpXPolygon->pFlagAry[nPos+3] = PolyFlags::Smooth;
267 nPos += 4;
270 else
272 pImpXPolygon->pPointAry[nPos++] = rRect.TopLeft();
273 pImpXPolygon->pPointAry[nPos++] = rRect.TopRight();
274 pImpXPolygon->pPointAry[nPos++] = rRect.BottomRight();
275 pImpXPolygon->pPointAry[nPos++] = rRect.BottomLeft();
277 pImpXPolygon->pPointAry[nPos] = pImpXPolygon->pPointAry[0];
278 pImpXPolygon->nPoints = nPos + 1;
281 /// create an ellipse (curve) as Bézier polygon
282 XPolygon::XPolygon(const Point& rCenter, tools::Long nRx, tools::Long nRy,
283 Degree100 nStartAngle, Degree100 nEndAngle, bool bClose)
284 : pImpXPolygon( 17 )
286 nStartAngle %= 36000_deg100;
287 if ( nEndAngle > 36000_deg100 ) nEndAngle %= 36000_deg100;
288 bool bFull = (nStartAngle == 0_deg100 && nEndAngle == 36000_deg100);
290 // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
291 tools::Long nXHdl = static_cast<tools::Long>(0.552284749 * nRx);
292 tools::Long nYHdl = static_cast<tools::Long>(0.552284749 * nRy);
293 sal_uInt16 nPos = 0;
294 bool bLoopEnd = false;
298 Degree100 nA1, nA2;
299 sal_uInt16 nQuad = nStartAngle.get() / 9000;
300 if ( nQuad == 4 ) nQuad = 0;
301 bLoopEnd = CheckAngles(nStartAngle, nEndAngle, nA1, nA2);
302 GenBezArc(rCenter, nRx, nRy, nXHdl, nYHdl, nA1, nA2, nQuad, nPos);
303 nPos += 3;
304 if ( !bLoopEnd )
305 pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
307 } while ( !bLoopEnd );
309 // if not a full circle then connect edges with center point if necessary
310 if ( !bFull && bClose )
311 pImpXPolygon->pPointAry[++nPos] = rCenter;
313 if ( bFull )
315 pImpXPolygon->pFlagAry[0 ] = PolyFlags::Smooth;
316 pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
318 pImpXPolygon->nPoints = nPos + 1;
321 XPolygon::~XPolygon() = default;
323 void XPolygon::SetPointCount( sal_uInt16 nPoints )
325 pImpXPolygon->CheckPointDelete();
327 if( pImpXPolygon->nSize < nPoints )
328 pImpXPolygon->Resize( nPoints );
330 if ( nPoints < pImpXPolygon->nPoints )
332 sal_uInt16 nSize = pImpXPolygon->nPoints - nPoints;
333 std::fill(
334 pImpXPolygon->pPointAry.get() + nPoints, pImpXPolygon->pPointAry.get() + nPoints + nSize, Point());
335 memset( &pImpXPolygon->pFlagAry [nPoints], 0, nSize );
337 pImpXPolygon->nPoints = nPoints;
340 sal_uInt16 XPolygon::GetSize() const
342 pImpXPolygon->CheckPointDelete();
343 return pImpXPolygon->nSize;
346 sal_uInt16 XPolygon::GetPointCount() const
348 pImpXPolygon->CheckPointDelete();
349 return pImpXPolygon->nPoints;
352 void XPolygon::Insert( sal_uInt16 nPos, const Point& rPt, PolyFlags eFlags )
354 if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
355 pImpXPolygon->InsertSpace( nPos, 1 );
356 pImpXPolygon->pPointAry[nPos] = rPt;
357 pImpXPolygon->pFlagAry[nPos] = eFlags;
360 void XPolygon::Insert( sal_uInt16 nPos, const XPolygon& rXPoly )
362 if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
364 sal_uInt16 nPoints = rXPoly.GetPointCount();
366 pImpXPolygon->InsertSpace( nPos, nPoints );
368 memcpy( &(pImpXPolygon->pPointAry[nPos]),
369 rXPoly.pImpXPolygon->pPointAry.get(),
370 nPoints*sizeof( Point ) );
371 memcpy( &(pImpXPolygon->pFlagAry[nPos]),
372 rXPoly.pImpXPolygon->pFlagAry.get(),
373 nPoints );
376 void XPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
378 pImpXPolygon->Remove( nPos, nCount );
381 void XPolygon::Move( tools::Long nHorzMove, tools::Long nVertMove )
383 if ( !nHorzMove && !nVertMove )
384 return;
386 // move points
387 sal_uInt16 nCount = pImpXPolygon->nPoints;
388 for ( sal_uInt16 i = 0; i < nCount; i++ )
390 Point* pPt = &(pImpXPolygon->pPointAry[i]);
391 pPt->AdjustX( nHorzMove );
392 pPt->AdjustY( nVertMove );
396 tools::Rectangle XPolygon::GetBoundRect() const
398 pImpXPolygon->CheckPointDelete();
399 tools::Rectangle aRetval;
401 if(pImpXPolygon->nPoints)
403 // #i37709#
404 // For historical reasons the control points are not part of the
405 // BoundRect. This makes it necessary to subdivide the polygon to
406 // get a relatively correct BoundRect. Numerically, this is not
407 // correct and never was.
409 const basegfx::B2DRange aPolygonRange(basegfx::utils::getRange(getB2DPolygon()));
410 aRetval = tools::Rectangle(
411 FRound(aPolygonRange.getMinX()), FRound(aPolygonRange.getMinY()),
412 FRound(aPolygonRange.getMaxX()), FRound(aPolygonRange.getMaxY()));
415 return aRetval;
418 const Point& XPolygon::operator[]( sal_uInt16 nPos ) const
420 DBG_ASSERT(nPos < pImpXPolygon->nPoints, "Invalid index at const array access to XPolygon");
422 pImpXPolygon->CheckPointDelete();
423 return pImpXPolygon->pPointAry[nPos];
426 Point& XPolygon::operator[]( sal_uInt16 nPos )
428 pImpXPolygon->CheckPointDelete();
430 if( nPos >= pImpXPolygon->nSize )
432 DBG_ASSERT(pImpXPolygon->nResize, "Invalid index at array access to XPolygon");
433 pImpXPolygon->Resize(nPos + 1, false);
435 if( nPos >= pImpXPolygon->nPoints )
436 pImpXPolygon->nPoints = nPos + 1;
438 return pImpXPolygon->pPointAry[nPos];
441 XPolygon& XPolygon::operator=( const XPolygon& ) = default;
443 XPolygon& XPolygon::operator=( XPolygon&& ) = default;
445 bool XPolygon::operator==( const XPolygon& rXPoly ) const
447 pImpXPolygon->CheckPointDelete();
448 return rXPoly.pImpXPolygon == pImpXPolygon;
451 /// get the flags for the point at the given position
452 PolyFlags XPolygon::GetFlags( sal_uInt16 nPos ) const
454 pImpXPolygon->CheckPointDelete();
455 return pImpXPolygon->pFlagAry[nPos];
458 /// set the flags for the point at the given position
459 void XPolygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags )
461 pImpXPolygon->CheckPointDelete();
462 pImpXPolygon->pFlagAry[nPos] = eFlags;
465 /// short path to read the CONTROL flag directly (TODO: better explain what the sense behind this flag is!)
466 bool XPolygon::IsControl(sal_uInt16 nPos) const
468 return pImpXPolygon->pFlagAry[nPos] == PolyFlags::Control;
471 /// short path to read the SMOOTH and SYMMTR flag directly (TODO: better explain what the sense behind these flags is!)
472 bool XPolygon::IsSmooth(sal_uInt16 nPos) const
474 PolyFlags eFlag = pImpXPolygon->pFlagAry[nPos];
475 return ( eFlag == PolyFlags::Smooth || eFlag == PolyFlags::Symmetric );
478 /** calculate the euclidean distance between two points
480 * @param nP1 The first point
481 * @param nP2 The second point
483 double XPolygon::CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
485 const Point& rP1 = pImpXPolygon->pPointAry[nP1];
486 const Point& rP2 = pImpXPolygon->pPointAry[nP2];
487 double fDx = rP2.X() - rP1.X();
488 double fDy = rP2.Y() - rP1.Y();
489 return sqrt(fDx * fDx + fDy * fDy);
492 void XPolygon::SubdivideBezier(sal_uInt16 nPos, bool bCalcFirst, double fT)
494 Point* pPoints = pImpXPolygon->pPointAry.get();
495 double fT2 = fT * fT;
496 double fT3 = fT * fT2;
497 double fU = 1.0 - fT;
498 double fU2 = fU * fU;
499 double fU3 = fU * fU2;
500 sal_uInt16 nIdx = nPos;
501 short nPosInc, nIdxInc;
503 if ( bCalcFirst )
505 nPos += 3;
506 nPosInc = -1;
507 nIdxInc = 0;
509 else
511 nPosInc = 1;
512 nIdxInc = 1;
514 pPoints[nPos].setX( static_cast<tools::Long>(fU3 * pPoints[nIdx ].X() +
515 fT * fU2 * pPoints[nIdx+1].X() * 3 +
516 fT2 * fU * pPoints[nIdx+2].X() * 3 +
517 fT3 * pPoints[nIdx+3].X()) );
518 pPoints[nPos].setY( static_cast<tools::Long>(fU3 * pPoints[nIdx ].Y() +
519 fT * fU2 * pPoints[nIdx+1].Y() * 3 +
520 fT2 * fU * pPoints[nIdx+2].Y() * 3 +
521 fT3 * pPoints[nIdx+3].Y()) );
522 nPos = nPos + nPosInc;
523 nIdx = nIdx + nIdxInc;
524 pPoints[nPos].setX( static_cast<tools::Long>(fU2 * pPoints[nIdx ].X() +
525 fT * fU * pPoints[nIdx+1].X() * 2 +
526 fT2 * pPoints[nIdx+2].X()) );
527 pPoints[nPos].setY( static_cast<tools::Long>(fU2 * pPoints[nIdx ].Y() +
528 fT * fU * pPoints[nIdx+1].Y() * 2 +
529 fT2 * pPoints[nIdx+2].Y()) );
530 nPos = nPos + nPosInc;
531 nIdx = nIdx + nIdxInc;
532 pPoints[nPos].setX( static_cast<tools::Long>(fU * pPoints[nIdx ].X() +
533 fT * pPoints[nIdx+1].X()) );
534 pPoints[nPos].setY( static_cast<tools::Long>(fU * pPoints[nIdx ].Y() +
535 fT * pPoints[nIdx+1].Y()) );
538 /// Generate a Bézier arc
539 void XPolygon::GenBezArc(const Point& rCenter, tools::Long nRx, tools::Long nRy,
540 tools::Long nXHdl, tools::Long nYHdl, Degree100 nStart, Degree100 nEnd,
541 sal_uInt16 nQuad, sal_uInt16 nFirst)
543 Point* pPoints = pImpXPolygon->pPointAry.get();
544 pPoints[nFirst ] = rCenter;
545 pPoints[nFirst+3] = rCenter;
547 if ( nQuad == 1 || nQuad == 2 )
549 nRx = -nRx; nXHdl = -nXHdl;
551 if ( nQuad == 0 || nQuad == 1 )
553 nRy = -nRy; nYHdl = -nYHdl;
556 if ( nQuad == 0 || nQuad == 2 )
558 pPoints[nFirst].AdjustX( nRx );
559 pPoints[nFirst+3].AdjustY( nRy );
561 else
563 pPoints[nFirst].AdjustY( nRy );
564 pPoints[nFirst+3].AdjustX( nRx );
566 pPoints[nFirst+1] = pPoints[nFirst];
567 pPoints[nFirst+2] = pPoints[nFirst+3];
569 if ( nQuad == 0 || nQuad == 2 )
571 pPoints[nFirst+1].AdjustY( nYHdl );
572 pPoints[nFirst+2].AdjustX( nXHdl );
574 else
576 pPoints[nFirst+1].AdjustX( nXHdl );
577 pPoints[nFirst+2].AdjustY( nYHdl );
579 if ( nStart > 0_deg100 )
580 SubdivideBezier(nFirst, false, static_cast<double>(nStart.get()) / 9000);
581 if ( nEnd < 9000_deg100 )
582 SubdivideBezier(nFirst, true, static_cast<double>((nEnd-nStart).get()) / (9000_deg100-nStart).get());
583 SetFlags(nFirst+1, PolyFlags::Control);
584 SetFlags(nFirst+2, PolyFlags::Control);
587 bool XPolygon::CheckAngles(Degree100& nStart, Degree100 nEnd, Degree100& nA1, Degree100& nA2)
589 if ( nStart == 36000_deg100 ) nStart = 0_deg100;
590 if ( nEnd == 0_deg100 ) nEnd = 36000_deg100;
591 Degree100 nStPrev = nStart;
592 Degree100 nMax((nStart.get() / 9000 + 1) * 9000);
593 Degree100 nMin = nMax - 9000_deg100;
595 if ( nEnd >= nMax || nEnd <= nStart ) nA2 = 9000_deg100;
596 else nA2 = nEnd - nMin;
597 nA1 = nStart - nMin;
598 nStart = nMax;
600 // returns true when the last segment was calculated
601 return (nStPrev < nEnd && nStart >= nEnd);
604 /** Calculate a smooth transition to connect two Bézier curves
606 * This is done by projecting the corresponding point onto a line between
607 * two other points.
609 * @param nCenter The point at the end or beginning of the curve.
610 * If nCenter is at the end of the polygon the point is moved
611 * to the opposite side.
612 * @param nDrag The moved point that specifies the relocation.
613 * @param nPnt The point to modify.
615 void XPolygon::CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
617 // If nPoint is no control point, i.e. cannot be moved, then
618 // move nDrag instead on the line between nCenter and nPnt
619 if ( !IsControl(nPnt) )
621 sal_uInt16 nTmp = nDrag;
622 nDrag = nPnt;
623 nPnt = nTmp;
625 Point* pPoints = pImpXPolygon->pPointAry.get();
626 Point aDiff = pPoints[nDrag] - pPoints[nCenter];
627 double fDiv = CalcDistance(nCenter, nDrag);
629 if ( fDiv )
631 double fRatio = CalcDistance(nCenter, nPnt) / fDiv;
632 // keep the length if SMOOTH
633 if ( GetFlags(nCenter) == PolyFlags::Smooth || !IsControl(nDrag) )
635 aDiff.setX( static_cast<tools::Long>(fRatio * aDiff.X()) );
636 aDiff.setY( static_cast<tools::Long>(fRatio * aDiff.Y()) );
638 pPoints[nPnt] = pPoints[nCenter] - aDiff;
642 /** Calculate tangent between two Bézier curves
644 * @param nCenter start or end point of the curves
645 * @param nPrev previous reference point
646 * @param nNext next reference point
648 void XPolygon::CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
650 double fAbsLen = CalcDistance(nNext, nPrev);
652 if ( !fAbsLen )
653 return;
655 const Point& rCenter = pImpXPolygon->pPointAry[nCenter];
656 Point& rNext = pImpXPolygon->pPointAry[nNext];
657 Point& rPrev = pImpXPolygon->pPointAry[nPrev];
658 Point aDiff = rNext - rPrev;
659 double fNextLen = CalcDistance(nCenter, nNext) / fAbsLen;
660 double fPrevLen = CalcDistance(nCenter, nPrev) / fAbsLen;
662 // same length for both sides if SYMMTR
663 if ( GetFlags(nCenter) == PolyFlags::Symmetric )
665 fPrevLen = (fNextLen + fPrevLen) / 2;
666 fNextLen = fPrevLen;
668 rNext.setX( rCenter.X() + static_cast<tools::Long>(fNextLen * aDiff.X()) );
669 rNext.setY( rCenter.Y() + static_cast<tools::Long>(fNextLen * aDiff.Y()) );
670 rPrev.setX( rCenter.X() - static_cast<tools::Long>(fPrevLen * aDiff.X()) );
671 rPrev.setY( rCenter.Y() - static_cast<tools::Long>(fPrevLen * aDiff.Y()) );
674 /// convert four polygon points into a Bézier curve
675 void XPolygon::PointsToBezier(sal_uInt16 nFirst)
677 double nFullLength, nPart1Length, nPart2Length;
678 double fX0, fY0, fX1, fY1, fX2, fY2, fX3, fY3;
679 double fTx1, fTx2, fTy1, fTy2;
680 double fT1, fU1, fT2, fU2, fV;
681 Point* pPoints = pImpXPolygon->pPointAry.get();
683 if ( nFirst > pImpXPolygon->nPoints - 4 || IsControl(nFirst) ||
684 IsControl(nFirst+1) || IsControl(nFirst+2) || IsControl(nFirst+3) )
685 return;
687 fTx1 = pPoints[nFirst+1].X();
688 fTy1 = pPoints[nFirst+1].Y();
689 fTx2 = pPoints[nFirst+2].X();
690 fTy2 = pPoints[nFirst+2].Y();
691 fX0 = pPoints[nFirst ].X();
692 fY0 = pPoints[nFirst ].Y();
693 fX3 = pPoints[nFirst+3].X();
694 fY3 = pPoints[nFirst+3].Y();
696 nPart1Length = CalcDistance(nFirst, nFirst+1);
697 nPart2Length = nPart1Length + CalcDistance(nFirst+1, nFirst+2);
698 nFullLength = nPart2Length + CalcDistance(nFirst+2, nFirst+3);
699 if ( nFullLength < 20 )
700 return;
702 if ( nPart2Length == nFullLength )
703 nPart2Length -= 1;
704 if ( nPart1Length == nFullLength )
705 nPart1Length = nPart2Length - 1;
706 if ( nPart1Length <= 0 )
707 nPart1Length = 1;
708 if ( nPart2Length <= 0 || nPart2Length == nPart1Length )
709 nPart2Length = nPart1Length + 1;
711 fT1 = nPart1Length / nFullLength;
712 fU1 = 1.0 - fT1;
713 fT2 = nPart2Length / nFullLength;
714 fU2 = 1.0 - fT2;
715 fV = 3 * (1.0 - (fT1 * fU2) / (fT2 * fU1));
717 fX1 = fTx1 / (fT1 * fU1 * fU1) - fTx2 * fT1 / (fT2 * fT2 * fU1 * fU2);
718 fX1 /= fV;
719 fX1 -= fX0 * ( fU1 / fT1 + fU2 / fT2) / 3;
720 fX1 += fX3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
722 fY1 = fTy1 / (fT1 * fU1 * fU1) - fTy2 * fT1 / (fT2 * fT2 * fU1 * fU2);
723 fY1 /= fV;
724 fY1 -= fY0 * ( fU1 / fT1 + fU2 / fT2) / 3;
725 fY1 += fY3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
727 fX2 = fTx2 / (fT2 * fT2 * fU2 * 3) - fX0 * fU2 * fU2 / ( fT2 * fT2 * 3);
728 fX2 -= fX1 * fU2 / fT2;
729 fX2 -= fX3 * fT2 / (fU2 * 3);
731 fY2 = fTy2 / (fT2 * fT2 * fU2 * 3) - fY0 * fU2 * fU2 / ( fT2 * fT2 * 3);
732 fY2 -= fY1 * fU2 / fT2;
733 fY2 -= fY3 * fT2 / (fU2 * 3);
735 pPoints[nFirst+1] = Point(static_cast<tools::Long>(fX1), static_cast<tools::Long>(fY1));
736 pPoints[nFirst+2] = Point(static_cast<tools::Long>(fX2), static_cast<tools::Long>(fY2));
737 SetFlags(nFirst+1, PolyFlags::Control);
738 SetFlags(nFirst+2, PolyFlags::Control);
741 /// scale in X- and/or Y-direction
742 void XPolygon::Scale(double fSx, double fSy)
744 pImpXPolygon->CheckPointDelete();
746 sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
748 for (sal_uInt16 i = 0; i < nPntCnt; i++)
750 Point& rPnt = pImpXPolygon->pPointAry[i];
751 rPnt.setX( static_cast<tools::Long>(fSx * rPnt.X()) );
752 rPnt.setY( static_cast<tools::Long>(fSy * rPnt.Y()) );
757 * Distort a polygon by scaling its coordinates relative to a reference
758 * rectangle into an arbitrary rectangle.
760 * Mapping between polygon corners and reference rectangle:
761 * 0: top left 0----1
762 * 1: top right | |
763 * 2: bottom right 3----2
764 * 3: bottom left
766 void XPolygon::Distort(const tools::Rectangle& rRefRect,
767 const XPolygon& rDistortedRect)
769 pImpXPolygon->CheckPointDelete();
771 tools::Long Xr, Wr;
772 tools::Long Yr, Hr;
774 Xr = rRefRect.Left();
775 Yr = rRefRect.Top();
776 Wr = rRefRect.GetWidth();
777 Hr = rRefRect.GetHeight();
779 if ( !Wr || !Hr )
780 return;
782 tools::Long X1, X2, X3, X4;
783 tools::Long Y1, Y2, Y3, Y4;
784 DBG_ASSERT(rDistortedRect.pImpXPolygon->nPoints >= 4,
785 "Distort: rectangle too small");
787 X1 = rDistortedRect[0].X();
788 Y1 = rDistortedRect[0].Y();
789 X2 = rDistortedRect[1].X();
790 Y2 = rDistortedRect[1].Y();
791 X3 = rDistortedRect[3].X();
792 Y3 = rDistortedRect[3].Y();
793 X4 = rDistortedRect[2].X();
794 Y4 = rDistortedRect[2].Y();
796 sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
798 for (sal_uInt16 i = 0; i < nPntCnt; i++)
800 double fTx, fTy, fUx, fUy;
801 Point& rPnt = pImpXPolygon->pPointAry[i];
803 fTx = static_cast<double>(rPnt.X() - Xr) / Wr;
804 fTy = static_cast<double>(rPnt.Y() - Yr) / Hr;
805 fUx = 1.0 - fTx;
806 fUy = 1.0 - fTy;
808 rPnt.setX( static_cast<tools::Long>( fUy * (fUx * X1 + fTx * X2) +
809 fTy * (fUx * X3 + fTx * X4) ) );
810 rPnt.setY( static_cast<tools::Long>( fUx * (fUy * Y1 + fTy * Y3) +
811 fTx * (fUy * Y2 + fTy * Y4) ) );
815 basegfx::B2DPolygon XPolygon::getB2DPolygon() const
817 // #i74631# use tools Polygon class for conversion to not have the code doubled
818 // here. This needs one more conversion but avoids different converters in
819 // the long run
820 const tools::Polygon aSource(GetPointCount(), pImpXPolygon->pPointAry.get(), pImpXPolygon->pFlagAry.get());
822 return aSource.getB2DPolygon();
825 XPolygon::XPolygon(const basegfx::B2DPolygon& rPolygon)
826 : pImpXPolygon( tools::Polygon( rPolygon ).GetSize() )
828 // #i74631# use tools Polygon class for conversion to not have the code doubled
829 // here. This needs one more conversion but avoids different converters in
830 // the long run
832 const tools::Polygon aSource(rPolygon);
833 sal_uInt16 nSize = aSource.GetSize();
834 pImpXPolygon->nPoints = nSize;
836 for( sal_uInt16 i = 0; i < nSize; i++ )
838 pImpXPolygon->pPointAry[i] = aSource[i];
839 pImpXPolygon->pFlagAry[i] = aSource.GetFlags( i );
843 // XPolyPolygon
844 XPolyPolygon::XPolyPolygon() = default;
846 XPolyPolygon::XPolyPolygon( const XPolyPolygon& ) = default;
848 XPolyPolygon::XPolyPolygon( XPolyPolygon&& ) = default;
850 XPolyPolygon::XPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
851 : pImpXPolyPolygon()
853 for(auto const& rCandidate : rPolyPolygon)
855 Insert(XPolygon(rCandidate));
859 XPolyPolygon::~XPolyPolygon() = default;
861 void XPolyPolygon::Insert( XPolygon&& rXPoly )
863 pImpXPolyPolygon->aXPolyList.emplace_back( std::move(rXPoly) );
866 /// insert all XPolygons of a XPolyPolygon
867 void XPolyPolygon::Insert( const XPolyPolygon& rXPolyPoly )
869 for ( size_t i = 0; i < rXPolyPoly.Count(); i++)
871 pImpXPolyPolygon->aXPolyList.emplace_back( rXPolyPoly[i] );
875 void XPolyPolygon::Remove( sal_uInt16 nPos )
877 pImpXPolyPolygon->aXPolyList.erase( pImpXPolyPolygon->aXPolyList.begin() + nPos );
880 const XPolygon& XPolyPolygon::GetObject( sal_uInt16 nPos ) const
882 return pImpXPolyPolygon->aXPolyList[ nPos ];
885 void XPolyPolygon::Clear()
887 pImpXPolyPolygon->aXPolyList.clear();
890 sal_uInt16 XPolyPolygon::Count() const
892 return static_cast<sal_uInt16>(pImpXPolyPolygon->aXPolyList.size());
895 tools::Rectangle XPolyPolygon::GetBoundRect() const
897 size_t nXPoly = pImpXPolyPolygon->aXPolyList.size();
898 tools::Rectangle aRect;
900 for ( size_t n = 0; n < nXPoly; n++ )
902 XPolygon const & rXPoly = pImpXPolyPolygon->aXPolyList[ n ];
903 aRect.Union( rXPoly.GetBoundRect() );
906 return aRect;
909 XPolygon& XPolyPolygon::operator[]( sal_uInt16 nPos )
911 return pImpXPolyPolygon->aXPolyList[ nPos ];
914 XPolyPolygon& XPolyPolygon::operator=( const XPolyPolygon& ) = default;
916 XPolyPolygon& XPolyPolygon::operator=( XPolyPolygon&& ) = default;
919 * Distort a polygon by scaling its coordinates relative to a reference
920 * rectangle into an arbitrary rectangle.
922 * Mapping between polygon corners and reference rectangle:
923 * 0: top left 0----1
924 * 1: top right | |
925 * 2: bottom right 3----2
926 * 3: bottom left
928 void XPolyPolygon::Distort(const tools::Rectangle& rRefRect,
929 const XPolygon& rDistortedRect)
931 for (size_t i = 0; i < Count(); i++)
932 pImpXPolyPolygon->aXPolyList[ i ].Distort(rRefRect, rDistortedRect);
935 basegfx::B2DPolyPolygon XPolyPolygon::getB2DPolyPolygon() const
937 basegfx::B2DPolyPolygon aRetval;
939 for(sal_uInt16 a(0); a < Count(); a++)
941 const XPolygon& rPoly = (*this)[a];
942 aRetval.append(rPoly.getB2DPolygon());
945 return aRetval;
948 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */