LibreOffice Module svx (master)  1
_xpoly.cxx
Go to the documentation of this file.
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 <sal/config.h>
21 
22 #include <algorithm>
23 
24 #include <osl/endian.h>
25 #include <tools/stream.hxx>
26 #include <tools/debug.hxx>
27 #include <tools/poly.hxx>
28 #include <tools/helpers.hxx>
29 #include <tools/gen.hxx>
30 
31 #include <svx/xpoly.hxx>
32 #include <xpolyimp.hxx>
39 
40 
41 ImpXPolygon::ImpXPolygon(sal_uInt16 nInitSize, sal_uInt16 _nResize)
42  : pOldPointAry(nullptr)
43  , bDeleteOldPoints(false)
44  , nSize(0)
45  , nResize(_nResize)
46  , nPoints(0)
47 {
48  Resize(nInitSize);
49 }
50 
52  : pOldPointAry(nullptr)
53  , bDeleteOldPoints(false)
54  , nSize(0)
55  , nResize(rImpXPoly.nResize)
56  , nPoints(0)
57 {
58  rImpXPoly.CheckPointDelete();
59 
60  Resize( rImpXPoly.nSize );
61 
62  // copy
63  nPoints = rImpXPoly.nPoints;
64  memcpy( pPointAry.get(), rImpXPoly.pPointAry.get(), nSize*sizeof( Point ) );
65  memcpy( pFlagAry.get(), rImpXPoly.pFlagAry.get(), nSize );
66 }
67 
69 {
70  pPointAry.reset();
71  if ( bDeleteOldPoints )
72  {
73  delete[] pOldPointAry;
74  pOldPointAry = nullptr;
75  }
76 }
77 
78 bool ImpXPolygon::operator==(const ImpXPolygon& rImpXPoly) const
79 {
80  return nPoints==rImpXPoly.nPoints &&
81  (nPoints==0 ||
82  (memcmp(pPointAry.get(), rImpXPoly.pPointAry.get(), nPoints*sizeof(Point))==0 &&
83  memcmp(pFlagAry.get(), rImpXPoly.pFlagAry.get(), nPoints)==0));
84 }
85 
94 void ImpXPolygon::Resize( sal_uInt16 nNewSize, bool bDeletePoints )
95 {
96  if( nNewSize == nSize )
97  return;
98 
99  PolyFlags* pOldFlagAry = pFlagAry.release();
100  sal_uInt16 nOldSize = nSize;
101 
103  pOldPointAry = pPointAry.release();
104 
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 )
108  {
109  DBG_ASSERT(nResize, "Trying to resize but nResize = 0 !");
110  nNewSize = nSize + ((nNewSize-nSize-1) / nResize + 1) * nResize;
111  }
112  // create point array
113  nSize = nNewSize;
114  pPointAry.reset( new Point[ nSize ] );
115 
116  // create flag array
117  pFlagAry.reset( new PolyFlags[ nSize ] );
118  memset( pFlagAry.get(), 0, nSize );
119 
120  // copy if needed
121  if( nOldSize )
122  {
123  if( nOldSize < nSize )
124  {
125  memcpy( pPointAry.get(), pOldPointAry, nOldSize*sizeof( Point ) );
126  memcpy( pFlagAry.get(), pOldFlagAry, nOldSize );
127  }
128  else
129  {
130  memcpy( pPointAry.get(), pOldPointAry, nSize*sizeof( Point ) );
131  memcpy( pFlagAry.get(), pOldFlagAry, nSize );
132 
133  // adjust number of valid points
134  if( nPoints > nSize )
135  nPoints = nSize;
136  }
137  if ( bDeletePoints )
138  {
139  delete[] pOldPointAry;
140  pOldPointAry = nullptr;
141  }
142  else
143  bDeleteOldPoints = true;
144  delete[] pOldFlagAry;
145  }
146 }
147 
148 void ImpXPolygon::InsertSpace( sal_uInt16 nPos, sal_uInt16 nCount )
149 {
151 
152  if ( nPos > nPoints )
153  nPos = nPoints;
154 
155  // if the polygon is too small then enlarge it
156  if( (nPoints + nCount) > nSize )
157  Resize( nPoints + nCount );
158 
159  // If the insert is not at the last position, move everything after backwards
160  if( nPos < nPoints )
161  {
162  sal_uInt16 nMove = nPoints - nPos;
163  memmove( &pPointAry[nPos+nCount], &pPointAry[nPos],
164  nMove * sizeof(Point) );
165  memmove( &pFlagAry[nPos+nCount], &pFlagAry[nPos], nMove );
166  }
167  std::fill(pPointAry.get() + nPos, pPointAry.get() + nPos + nCount, Point());
168  memset( &pFlagAry [nPos], 0, nCount );
169 
170  nPoints = nPoints + nCount;
171 }
172 
173 void ImpXPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
174 {
176 
177  if( (nPos + nCount) <= nPoints )
178  {
179  sal_uInt16 nMove = nPoints - nPos - nCount;
180 
181  if( nMove )
182  {
183  memmove( &pPointAry[nPos], &pPointAry[nPos+nCount],
184  nMove * sizeof(Point) );
185  memmove( &pFlagAry[nPos], &pFlagAry[nPos+nCount], nMove );
186  }
187  std::fill(pPointAry.get() + (nPoints - nCount), pPointAry.get() + nPoints, Point());
188  memset( &pFlagAry [nPoints - nCount], 0, nCount );
189  nPoints = nPoints - nCount;
190  }
191 }
192 
194 {
195  if ( bDeleteOldPoints )
196  {
197  delete[] pOldPointAry;
198  const_cast< ImpXPolygon* >(this)->pOldPointAry = nullptr;
199  const_cast< ImpXPolygon* >(this)->bDeleteOldPoints = false;
200  }
201 }
202 
203 XPolygon::XPolygon( sal_uInt16 nSize )
204  : pImpXPolygon( ImpXPolygon( nSize, 16 ) )
205 {
206 }
207 
208 XPolygon::XPolygon( const XPolygon& ) = default;
209 
210 XPolygon::XPolygon( XPolygon&& ) = default;
211 
214  : pImpXPolygon( rPoly.GetSize() )
215 {
216  sal_uInt16 nSize = rPoly.GetSize();
217  pImpXPolygon->nPoints = nSize;
218 
219  for( sal_uInt16 i = 0; i < nSize; i++ )
220  {
221  pImpXPolygon->pPointAry[i] = rPoly[i];
222  pImpXPolygon->pFlagAry[i] = rPoly.GetFlags( i );
223  }
224 }
225 
227 XPolygon::XPolygon(const tools::Rectangle& rRect, long nRx, long nRy)
228  : pImpXPolygon( 17 )
229 {
230  long nWh = (rRect.GetWidth() - 1) / 2;
231  long nHh = (rRect.GetHeight() - 1) / 2;
232 
233  if ( nRx > nWh ) nRx = nWh;
234  if ( nRy > nHh ) nRy = nHh;
235 
236  // negate Rx => circle clockwise
237  nRx = -nRx;
238 
239  // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
240  long nXHdl = static_cast<long>(0.552284749 * nRx);
241  long nYHdl = static_cast<long>(0.552284749 * nRy);
242  sal_uInt16 nPos = 0;
243 
244  if ( nRx && nRy )
245  {
246  Point aCenter;
247 
248  for (sal_uInt16 nQuad = 0; nQuad < 4; nQuad++)
249  {
250  switch ( nQuad )
251  {
252  case 0: aCenter = rRect.TopLeft();
253  aCenter.AdjustX( -nRx );
254  aCenter.AdjustY(nRy );
255  break;
256  case 1: aCenter = rRect.TopRight();
257  aCenter.AdjustX(nRx );
258  aCenter.AdjustY(nRy );
259  break;
260  case 2: aCenter = rRect.BottomRight();
261  aCenter.AdjustX(nRx );
262  aCenter.AdjustY( -nRy );
263  break;
264  case 3: aCenter = rRect.BottomLeft();
265  aCenter.AdjustX( -nRx );
266  aCenter.AdjustY( -nRy );
267  break;
268  }
269  GenBezArc(aCenter, nRx, nRy, nXHdl, nYHdl, 0, 900, nQuad, nPos);
270  pImpXPolygon->pFlagAry[nPos ] = PolyFlags::Smooth;
271  pImpXPolygon->pFlagAry[nPos+3] = PolyFlags::Smooth;
272  nPos += 4;
273  }
274  }
275  else
276  {
277  pImpXPolygon->pPointAry[nPos++] = rRect.TopLeft();
278  pImpXPolygon->pPointAry[nPos++] = rRect.TopRight();
279  pImpXPolygon->pPointAry[nPos++] = rRect.BottomRight();
280  pImpXPolygon->pPointAry[nPos++] = rRect.BottomLeft();
281  }
283  pImpXPolygon->nPoints = nPos + 1;
284 }
285 
287 XPolygon::XPolygon(const Point& rCenter, long nRx, long nRy,
288  sal_uInt16 nStartAngle, sal_uInt16 nEndAngle, bool bClose)
289  : pImpXPolygon( 17 )
290 {
291  nStartAngle %= 3600;
292  if ( nEndAngle > 3600 ) nEndAngle %= 3600;
293  bool bFull = (nStartAngle == 0 && nEndAngle == 3600);
294 
295  // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
296  long nXHdl = static_cast<long>(0.552284749 * nRx);
297  long nYHdl = static_cast<long>(0.552284749 * nRy);
298  sal_uInt16 nPos = 0;
299  bool bLoopEnd = false;
300 
301  do
302  {
303  sal_uInt16 nA1, nA2;
304  sal_uInt16 nQuad = nStartAngle / 900;
305  if ( nQuad == 4 ) nQuad = 0;
306  bLoopEnd = CheckAngles(nStartAngle, nEndAngle, nA1, nA2);
307  GenBezArc(rCenter, nRx, nRy, nXHdl, nYHdl, nA1, nA2, nQuad, nPos);
308  nPos += 3;
309  if ( !bLoopEnd )
310  pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
311 
312  } while ( !bLoopEnd );
313 
314  // if not a full circle then connect edges with center point if necessary
315  if ( !bFull && bClose )
316  pImpXPolygon->pPointAry[++nPos] = rCenter;
317 
318  if ( bFull )
319  {
320  pImpXPolygon->pFlagAry[0 ] = PolyFlags::Smooth;
321  pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
322  }
323  pImpXPolygon->nPoints = nPos + 1;
324 }
325 
326 XPolygon::~XPolygon() = default;
327 
328 void XPolygon::SetPointCount( sal_uInt16 nPoints )
329 {
331 
332  if( pImpXPolygon->nSize < nPoints )
333  pImpXPolygon->Resize( nPoints );
334 
335  if ( nPoints < pImpXPolygon->nPoints )
336  {
337  sal_uInt16 nSize = pImpXPolygon->nPoints - nPoints;
338  std::fill(
339  pImpXPolygon->pPointAry.get() + nPoints, pImpXPolygon->pPointAry.get() + nPoints + nSize, Point());
340  memset( &pImpXPolygon->pFlagAry [nPoints], 0, nSize );
341  }
342  pImpXPolygon->nPoints = nPoints;
343 }
344 
345 sal_uInt16 XPolygon::GetSize() const
346 {
348  return pImpXPolygon->nSize;
349 }
350 
351 sal_uInt16 XPolygon::GetPointCount() const
352 {
354  return pImpXPolygon->nPoints;
355 }
356 
357 void XPolygon::Insert( sal_uInt16 nPos, const Point& rPt, PolyFlags eFlags )
358 {
359  if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
360  pImpXPolygon->InsertSpace( nPos, 1 );
361  pImpXPolygon->pPointAry[nPos] = rPt;
362  pImpXPolygon->pFlagAry[nPos] = eFlags;
363 }
364 
365 void XPolygon::Insert( sal_uInt16 nPos, const XPolygon& rXPoly )
366 {
367  if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
368 
369  sal_uInt16 nPoints = rXPoly.GetPointCount();
370 
371  pImpXPolygon->InsertSpace( nPos, nPoints );
372 
373  memcpy( &(pImpXPolygon->pPointAry[nPos]),
374  rXPoly.pImpXPolygon->pPointAry.get(),
375  nPoints*sizeof( Point ) );
376  memcpy( &(pImpXPolygon->pFlagAry[nPos]),
377  rXPoly.pImpXPolygon->pFlagAry.get(),
378  nPoints );
379 }
380 
381 void XPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
382 {
383  pImpXPolygon->Remove( nPos, nCount );
384 }
385 
386 void XPolygon::Move( long nHorzMove, long nVertMove )
387 {
388  if ( !nHorzMove && !nVertMove )
389  return;
390 
391  // move points
392  sal_uInt16 nCount = pImpXPolygon->nPoints;
393  for ( sal_uInt16 i = 0; i < nCount; i++ )
394  {
395  Point* pPt = &(pImpXPolygon->pPointAry[i]);
396  pPt->AdjustX( nHorzMove );
397  pPt->AdjustY( nVertMove );
398  }
399 }
400 
402 {
404  tools::Rectangle aRetval;
405 
406  if(pImpXPolygon->nPoints)
407  {
408  // #i37709#
409  // For historical reasons the control points are not part of the
410  // BoundRect. This makes it necessary to subdivide the polygon to
411  // get a relatively correct BoundRect. Numerically, this is not
412  // correct and never was.
413 
415  aRetval = tools::Rectangle(
416  FRound(aPolygonRange.getMinX()), FRound(aPolygonRange.getMinY()),
417  FRound(aPolygonRange.getMaxX()), FRound(aPolygonRange.getMaxY()));
418  }
419 
420  return aRetval;
421 }
422 
423 const Point& XPolygon::operator[]( sal_uInt16 nPos ) const
424 {
425  DBG_ASSERT(nPos < pImpXPolygon->nPoints, "Invalid index at const array access to XPolygon");
426 
428  return pImpXPolygon->pPointAry[nPos];
429 }
430 
431 Point& XPolygon::operator[]( sal_uInt16 nPos )
432 {
434 
435  if( nPos >= pImpXPolygon->nSize )
436  {
437  DBG_ASSERT(pImpXPolygon->nResize, "Invalid index at array access to XPolygon");
438  pImpXPolygon->Resize(nPos + 1, false);
439  }
440  if( nPos >= pImpXPolygon->nPoints )
441  pImpXPolygon->nPoints = nPos + 1;
442 
443  return pImpXPolygon->pPointAry[nPos];
444 }
445 
446 XPolygon& XPolygon::operator=( const XPolygon& ) = default;
447 
448 XPolygon& XPolygon::operator=( XPolygon&& ) = default;
449 
450 bool XPolygon::operator==( const XPolygon& rXPoly ) const
451 {
453  return rXPoly.pImpXPolygon == pImpXPolygon;
454 }
455 
457 PolyFlags XPolygon::GetFlags( sal_uInt16 nPos ) const
458 {
460  return pImpXPolygon->pFlagAry[nPos];
461 }
462 
464 void XPolygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags )
465 {
467  pImpXPolygon->pFlagAry[nPos] = eFlags;
468 }
469 
471 bool XPolygon::IsControl(sal_uInt16 nPos) const
472 {
473  return pImpXPolygon->pFlagAry[nPos] == PolyFlags::Control;
474 }
475 
477 bool XPolygon::IsSmooth(sal_uInt16 nPos) const
478 {
479  PolyFlags eFlag = pImpXPolygon->pFlagAry[nPos];
480  return ( eFlag == PolyFlags::Smooth || eFlag == PolyFlags::Symmetric );
481 }
482 
488 double XPolygon::CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
489 {
490  const Point& rP1 = pImpXPolygon->pPointAry[nP1];
491  const Point& rP2 = pImpXPolygon->pPointAry[nP2];
492  double fDx = rP2.X() - rP1.X();
493  double fDy = rP2.Y() - rP1.Y();
494  return sqrt(fDx * fDx + fDy * fDy);
495 }
496 
497 void XPolygon::SubdivideBezier(sal_uInt16 nPos, bool bCalcFirst, double fT)
498 {
499  Point* pPoints = pImpXPolygon->pPointAry.get();
500  double fT2 = fT * fT;
501  double fT3 = fT * fT2;
502  double fU = 1.0 - fT;
503  double fU2 = fU * fU;
504  double fU3 = fU * fU2;
505  sal_uInt16 nIdx = nPos;
506  short nPosInc, nIdxInc;
507 
508  if ( bCalcFirst )
509  {
510  nPos += 3;
511  nPosInc = -1;
512  nIdxInc = 0;
513  }
514  else
515  {
516  nPosInc = 1;
517  nIdxInc = 1;
518  }
519  pPoints[nPos].setX( static_cast<long>(fU3 * pPoints[nIdx ].X() +
520  fT * fU2 * pPoints[nIdx+1].X() * 3 +
521  fT2 * fU * pPoints[nIdx+2].X() * 3 +
522  fT3 * pPoints[nIdx+3].X()) );
523  pPoints[nPos].setY( static_cast<long>(fU3 * pPoints[nIdx ].Y() +
524  fT * fU2 * pPoints[nIdx+1].Y() * 3 +
525  fT2 * fU * pPoints[nIdx+2].Y() * 3 +
526  fT3 * pPoints[nIdx+3].Y()) );
527  nPos = nPos + nPosInc;
528  nIdx = nIdx + nIdxInc;
529  pPoints[nPos].setX( static_cast<long>(fU2 * pPoints[nIdx ].X() +
530  fT * fU * pPoints[nIdx+1].X() * 2 +
531  fT2 * pPoints[nIdx+2].X()) );
532  pPoints[nPos].setY( static_cast<long>(fU2 * pPoints[nIdx ].Y() +
533  fT * fU * pPoints[nIdx+1].Y() * 2 +
534  fT2 * pPoints[nIdx+2].Y()) );
535  nPos = nPos + nPosInc;
536  nIdx = nIdx + nIdxInc;
537  pPoints[nPos].setX( static_cast<long>(fU * pPoints[nIdx ].X() +
538  fT * pPoints[nIdx+1].X()) );
539  pPoints[nPos].setY( static_cast<long>(fU * pPoints[nIdx ].Y() +
540  fT * pPoints[nIdx+1].Y()) );
541 }
542 
544 void XPolygon::GenBezArc(const Point& rCenter, long nRx, long nRy,
545  long nXHdl, long nYHdl, sal_uInt16 nStart, sal_uInt16 nEnd,
546  sal_uInt16 nQuad, sal_uInt16 nFirst)
547 {
548  Point* pPoints = pImpXPolygon->pPointAry.get();
549  pPoints[nFirst ] = rCenter;
550  pPoints[nFirst+3] = rCenter;
551 
552  if ( nQuad == 1 || nQuad == 2 )
553  {
554  nRx = -nRx; nXHdl = -nXHdl;
555  }
556  if ( nQuad == 0 || nQuad == 1 )
557  {
558  nRy = -nRy; nYHdl = -nYHdl;
559  }
560 
561  if ( nQuad == 0 || nQuad == 2 )
562  {
563  pPoints[nFirst].AdjustX( nRx );
564  pPoints[nFirst+3].AdjustY( nRy );
565  }
566  else
567  {
568  pPoints[nFirst].AdjustY( nRy );
569  pPoints[nFirst+3].AdjustX( nRx );
570  }
571  pPoints[nFirst+1] = pPoints[nFirst];
572  pPoints[nFirst+2] = pPoints[nFirst+3];
573 
574  if ( nQuad == 0 || nQuad == 2 )
575  {
576  pPoints[nFirst+1].AdjustY( nYHdl );
577  pPoints[nFirst+2].AdjustX( nXHdl );
578  }
579  else
580  {
581  pPoints[nFirst+1].AdjustX( nXHdl );
582  pPoints[nFirst+2].AdjustY( nYHdl );
583  }
584  if ( nStart > 0 )
585  SubdivideBezier(nFirst, false, static_cast<double>(nStart) / 900);
586  if ( nEnd < 900 )
587  SubdivideBezier(nFirst, true, static_cast<double>(nEnd-nStart) / (900-nStart));
588  SetFlags(nFirst+1, PolyFlags::Control);
589  SetFlags(nFirst+2, PolyFlags::Control);
590 }
591 
592 bool XPolygon::CheckAngles(sal_uInt16& nStart, sal_uInt16 nEnd, sal_uInt16& nA1, sal_uInt16& nA2)
593 {
594  if ( nStart == 3600 ) nStart = 0;
595  if ( nEnd == 0 ) nEnd = 3600;
596  sal_uInt16 nStPrev = nStart;
597  sal_uInt16 nMax = (nStart / 900 + 1) * 900;
598  sal_uInt16 nMin = nMax - 900;
599 
600  if ( nEnd >= nMax || nEnd <= nStart ) nA2 = 900;
601  else nA2 = nEnd - nMin;
602  nA1 = nStart - nMin;
603  nStart = nMax;
604 
605  // returns true when the last segment was calculated
606  return (nStPrev < nEnd && nStart >= nEnd);
607 }
608 
620 void XPolygon::CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
621 {
622  // If nPoint is no control point, i.e. cannot be moved, then
623  // move nDrag instead on the line between nCenter and nPnt
624  if ( !IsControl(nPnt) )
625  {
626  sal_uInt16 nTmp = nDrag;
627  nDrag = nPnt;
628  nPnt = nTmp;
629  }
630  Point* pPoints = pImpXPolygon->pPointAry.get();
631  Point aDiff = pPoints[nDrag] - pPoints[nCenter];
632  double fDiv = CalcDistance(nCenter, nDrag);
633 
634  if ( fDiv )
635  {
636  double fRatio = CalcDistance(nCenter, nPnt) / fDiv;
637  // keep the length if SMOOTH
638  if ( GetFlags(nCenter) == PolyFlags::Smooth || !IsControl(nDrag) )
639  {
640  aDiff.setX( static_cast<long>(fRatio * aDiff.X()) );
641  aDiff.setY( static_cast<long>(fRatio * aDiff.Y()) );
642  }
643  pPoints[nPnt] = pPoints[nCenter] - aDiff;
644  }
645 }
646 
653 void XPolygon::CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
654 {
655  double fAbsLen = CalcDistance(nNext, nPrev);
656 
657  if ( !fAbsLen )
658  return;
659 
660  const Point& rCenter = pImpXPolygon->pPointAry[nCenter];
661  Point& rNext = pImpXPolygon->pPointAry[nNext];
662  Point& rPrev = pImpXPolygon->pPointAry[nPrev];
663  Point aDiff = rNext - rPrev;
664  double fNextLen = CalcDistance(nCenter, nNext) / fAbsLen;
665  double fPrevLen = CalcDistance(nCenter, nPrev) / fAbsLen;
666 
667  // same length for both sides if SYMMTR
668  if ( GetFlags(nCenter) == PolyFlags::Symmetric )
669  {
670  fPrevLen = (fNextLen + fPrevLen) / 2;
671  fNextLen = fPrevLen;
672  }
673  rNext.setX( rCenter.X() + static_cast<long>(fNextLen * aDiff.X()) );
674  rNext.setY( rCenter.Y() + static_cast<long>(fNextLen * aDiff.Y()) );
675  rPrev.setX( rCenter.X() - static_cast<long>(fPrevLen * aDiff.X()) );
676  rPrev.setY( rCenter.Y() - static_cast<long>(fPrevLen * aDiff.Y()) );
677 }
678 
680 void XPolygon::PointsToBezier(sal_uInt16 nFirst)
681 {
682  double nFullLength, nPart1Length, nPart2Length;
683  double fX0, fY0, fX1, fY1, fX2, fY2, fX3, fY3;
684  double fTx1, fTx2, fTy1, fTy2;
685  double fT1, fU1, fT2, fU2, fV;
686  Point* pPoints = pImpXPolygon->pPointAry.get();
687 
688  if ( nFirst > pImpXPolygon->nPoints - 4 || IsControl(nFirst) ||
689  IsControl(nFirst+1) || IsControl(nFirst+2) || IsControl(nFirst+3) )
690  return;
691 
692  fTx1 = pPoints[nFirst+1].X();
693  fTy1 = pPoints[nFirst+1].Y();
694  fTx2 = pPoints[nFirst+2].X();
695  fTy2 = pPoints[nFirst+2].Y();
696  fX0 = pPoints[nFirst ].X();
697  fY0 = pPoints[nFirst ].Y();
698  fX3 = pPoints[nFirst+3].X();
699  fY3 = pPoints[nFirst+3].Y();
700 
701  nPart1Length = CalcDistance(nFirst, nFirst+1);
702  nPart2Length = nPart1Length + CalcDistance(nFirst+1, nFirst+2);
703  nFullLength = nPart2Length + CalcDistance(nFirst+2, nFirst+3);
704  if ( nFullLength < 20 )
705  return;
706 
707  if ( nPart2Length == nFullLength )
708  nPart2Length -= 1;
709  if ( nPart1Length == nFullLength )
710  nPart1Length = nPart2Length - 1;
711  if ( nPart1Length <= 0 )
712  nPart1Length = 1;
713  if ( nPart2Length <= 0 || nPart2Length == nPart1Length )
714  nPart2Length = nPart1Length + 1;
715 
716  fT1 = nPart1Length / nFullLength;
717  fU1 = 1.0 - fT1;
718  fT2 = nPart2Length / nFullLength;
719  fU2 = 1.0 - fT2;
720  fV = 3 * (1.0 - (fT1 * fU2) / (fT2 * fU1));
721 
722  fX1 = fTx1 / (fT1 * fU1 * fU1) - fTx2 * fT1 / (fT2 * fT2 * fU1 * fU2);
723  fX1 /= fV;
724  fX1 -= fX0 * ( fU1 / fT1 + fU2 / fT2) / 3;
725  fX1 += fX3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
726 
727  fY1 = fTy1 / (fT1 * fU1 * fU1) - fTy2 * fT1 / (fT2 * fT2 * fU1 * fU2);
728  fY1 /= fV;
729  fY1 -= fY0 * ( fU1 / fT1 + fU2 / fT2) / 3;
730  fY1 += fY3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
731 
732  fX2 = fTx2 / (fT2 * fT2 * fU2 * 3) - fX0 * fU2 * fU2 / ( fT2 * fT2 * 3);
733  fX2 -= fX1 * fU2 / fT2;
734  fX2 -= fX3 * fT2 / (fU2 * 3);
735 
736  fY2 = fTy2 / (fT2 * fT2 * fU2 * 3) - fY0 * fU2 * fU2 / ( fT2 * fT2 * 3);
737  fY2 -= fY1 * fU2 / fT2;
738  fY2 -= fY3 * fT2 / (fU2 * 3);
739 
740  pPoints[nFirst+1] = Point(static_cast<long>(fX1), static_cast<long>(fY1));
741  pPoints[nFirst+2] = Point(static_cast<long>(fX2), static_cast<long>(fY2));
742  SetFlags(nFirst+1, PolyFlags::Control);
743  SetFlags(nFirst+2, PolyFlags::Control);
744 }
745 
747 void XPolygon::Scale(double fSx, double fSy)
748 {
750 
751  sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
752 
753  for (sal_uInt16 i = 0; i < nPntCnt; i++)
754  {
755  Point& rPnt = pImpXPolygon->pPointAry[i];
756  rPnt.setX( static_cast<long>(fSx * rPnt.X()) );
757  rPnt.setY( static_cast<long>(fSy * rPnt.Y()) );
758  }
759 }
760 
771 void XPolygon::Distort(const tools::Rectangle& rRefRect,
772  const XPolygon& rDistortedRect)
773 {
775 
776  long Xr, Wr;
777  long Yr, Hr;
778 
779  Xr = rRefRect.Left();
780  Yr = rRefRect.Top();
781  Wr = rRefRect.GetWidth();
782  Hr = rRefRect.GetHeight();
783 
784  if ( !Wr || !Hr )
785  return;
786 
787  long X1, X2, X3, X4;
788  long Y1, Y2, Y3, Y4;
789  DBG_ASSERT(rDistortedRect.pImpXPolygon->nPoints >= 4,
790  "Distort: rectangle too small");
791 
792  X1 = rDistortedRect[0].X();
793  Y1 = rDistortedRect[0].Y();
794  X2 = rDistortedRect[1].X();
795  Y2 = rDistortedRect[1].Y();
796  X3 = rDistortedRect[3].X();
797  Y3 = rDistortedRect[3].Y();
798  X4 = rDistortedRect[2].X();
799  Y4 = rDistortedRect[2].Y();
800 
801  sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
802 
803  for (sal_uInt16 i = 0; i < nPntCnt; i++)
804  {
805  double fTx, fTy, fUx, fUy;
806  Point& rPnt = pImpXPolygon->pPointAry[i];
807 
808  fTx = static_cast<double>(rPnt.X() - Xr) / Wr;
809  fTy = static_cast<double>(rPnt.Y() - Yr) / Hr;
810  fUx = 1.0 - fTx;
811  fUy = 1.0 - fTy;
812 
813  rPnt.setX( static_cast<long>( fUy * (fUx * X1 + fTx * X2) +
814  fTy * (fUx * X3 + fTx * X4) ) );
815  rPnt.setY( static_cast<long>( fUx * (fUy * Y1 + fTy * Y3) +
816  fTx * (fUy * Y2 + fTy * Y4) ) );
817  }
818 }
819 
821 {
822  // #i74631# use tools Polygon class for conversion to not have the code doubled
823  // here. This needs one more conversion but avoids different convertors in
824  // the long run
825  const tools::Polygon aSource(GetPointCount(), pImpXPolygon->pPointAry.get(), pImpXPolygon->pFlagAry.get());
826 
827  return aSource.getB2DPolygon();
828 }
829 
831  : pImpXPolygon( tools::Polygon( rPolygon ).GetSize() )
832 {
833  // #i74631# use tools Polygon class for conversion to not have the code doubled
834  // here. This needs one more conversion but avoids different convertors in
835  // the long run
836 
837  const tools::Polygon aSource(rPolygon);
838  sal_uInt16 nSize = aSource.GetSize();
839  pImpXPolygon->nPoints = nSize;
840 
841  for( sal_uInt16 i = 0; i < nSize; i++ )
842  {
843  pImpXPolygon->pPointAry[i] = aSource[i];
844  pImpXPolygon->pFlagAry[i] = aSource.GetFlags( i );
845  }
846 }
847 
848 // XPolyPolygon
849 XPolyPolygon::XPolyPolygon() = default;
850 
851 XPolyPolygon::XPolyPolygon( const XPolyPolygon& ) = default;
852 
854 
856  : pImpXPolyPolygon()
857 {
858  for(auto const& rCandidate : rPolyPolygon)
859  {
860  Insert(XPolygon(rCandidate));
861  }
862 }
863 
864 XPolyPolygon::~XPolyPolygon() = default;
865 
867 {
868  pImpXPolyPolygon->aXPolyList.emplace_back( std::move(rXPoly) );
869 }
870 
872 void XPolyPolygon::Insert( const XPolyPolygon& rXPolyPoly )
873 {
874  for ( size_t i = 0; i < rXPolyPoly.Count(); i++)
875  {
876  pImpXPolyPolygon->aXPolyList.emplace_back( rXPolyPoly[i] );
877  }
878 }
879 
880 void XPolyPolygon::Remove( sal_uInt16 nPos )
881 {
882  pImpXPolyPolygon->aXPolyList.erase( pImpXPolyPolygon->aXPolyList.begin() + nPos );
883 }
884 
885 const XPolygon& XPolyPolygon::GetObject( sal_uInt16 nPos ) const
886 {
887  return pImpXPolyPolygon->aXPolyList[ nPos ];
888 }
889 
891 {
892  pImpXPolyPolygon->aXPolyList.clear();
893 }
894 
895 sal_uInt16 XPolyPolygon::Count() const
896 {
897  return static_cast<sal_uInt16>(pImpXPolyPolygon->aXPolyList.size());
898 }
899 
901 {
902  size_t nXPoly = pImpXPolyPolygon->aXPolyList.size();
903  tools::Rectangle aRect;
904 
905  for ( size_t n = 0; n < nXPoly; n++ )
906  {
907  XPolygon const & rXPoly = pImpXPolyPolygon->aXPolyList[ n ];
908  aRect.Union( rXPoly.GetBoundRect() );
909  }
910 
911  return aRect;
912 }
913 
915 {
916  return pImpXPolyPolygon->aXPolyList[ nPos ];
917 }
918 
920 
922 
934  const XPolygon& rDistortedRect)
935 {
936  for (size_t i = 0; i < Count(); i++)
937  pImpXPolyPolygon->aXPolyList[ i ].Distort(rRefRect, rDistortedRect);
938 }
939 
941 {
942  basegfx::B2DPolyPolygon aRetval;
943 
944  for(sal_uInt16 a(0); a < Count(); a++)
945  {
946  const XPolygon& rPoly = (*this)[a];
947  aRetval.append(rPoly.getB2DPolygon());
948  }
949 
950  return aRetval;
951 }
952 
953 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
void Insert(XPolygon &&rXPoly)
Definition: _xpoly.cxx:866
long GetWidth() const
tools::Rectangle GetBoundRect() const
Definition: _xpoly.cxx:401
long GetHeight() const
sal_uInt16 Count() const
Definition: _xpoly.cxx:895
o3tl::cow_wrapper< ImpXPolyPolygon > pImpXPolyPolygon
Definition: xpoly.hxx:115
long FRound(double fVal)
long AdjustX(long nHorzMove)
PolyFlags GetFlags(sal_uInt16 nPos) const
get the flags for the point at the given position
Definition: _xpoly.cxx:457
Point BottomLeft() const
sal_uInt16 const nResize
Definition: xpolyimp.hxx:38
sal_Int64 n
basegfx::B2DPolyPolygon getB2DPolyPolygon() const
Definition: _xpoly.cxx:940
void SetPointCount(sal_uInt16 nPoints)
Definition: _xpoly.cxx:328
~ImpXPolygon()
Definition: _xpoly.cxx:68
const XPolygon & operator[](sal_uInt16 nPos) const
Definition: xpoly.hxx:134
double getMaxX() const
void CheckPointDelete() const
Definition: _xpoly.cxx:193
bool bDeleteOldPoints
Definition: xpolyimp.hxx:36
::std::vector< XPolygon > aXPolyList
Definition: xpolyimp.hxx:57
void Move(long nHorzMove, long nVertMove)
Definition: _xpoly.cxx:386
const XPolygon & GetObject(sal_uInt16 nPos) const
Definition: _xpoly.cxx:885
XPolygon(sal_uInt16 nSize=16)
Definition: _xpoly.cxx:203
void setX(long nX)
#define X
PolyFlags GetFlags(sal_uInt16 nPos) const
void CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
Calculate tangent between two Bézier curves.
Definition: _xpoly.cxx:653
int nCount
bool operator==(const ImpXPolygon &rImpXPoly) const
Definition: _xpoly.cxx:78
void CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
Calculate a smooth transition to connect two Bézier curves.
Definition: _xpoly.cxx:620
long Top() const
sal_uInt16 nSize
Definition: xpolyimp.hxx:37
double getMaxY() const
void setY(long nY)
const Point & operator[](sal_uInt16 nPos) const
Definition: _xpoly.cxx:423
Point * pOldPointAry
Definition: xpolyimp.hxx:35
Point BottomRight() const
#define DBG_ASSERT(sCon, aError)
long AdjustY(long nVertMove)
uno_Any a
void Clear()
Definition: _xpoly.cxx:890
void GenBezArc(const Point &rCenter, long nRx, long nRy, long nXHdl, long nYHdl, sal_uInt16 nStart, sal_uInt16 nEnd, sal_uInt16 nQuad, sal_uInt16 nFirst)
Generate a Bézier arc.
Definition: _xpoly.cxx:544
std::unique_ptr< PolyFlags[]> pFlagAry
Definition: xpolyimp.hxx:34
int i
void SubdivideBezier(sal_uInt16 nPos, bool bCalcFirst, double fT)
Definition: _xpoly.cxx:497
basegfx::B2DPolygon getB2DPolygon() const
Definition: _xpoly.cxx:820
void Scale(double fSx, double fSy)
scale in X- and/or Y-direction
Definition: _xpoly.cxx:747
double CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
calculate the euclidean distance between two points
Definition: _xpoly.cxx:488
void Resize(sal_uInt16 nNewSize, bool bDeletePoints=true)
Change polygon size.
Definition: _xpoly.cxx:94
sal_uInt16 GetSize() const
::basegfx::B2DPolygon getB2DPolygon() const
long X() const
#define Y
B2DRange getRange(const B2DPolygon &rCandidate)
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void Insert(sal_uInt16 nPos, const Point &rPt, PolyFlags eFlags)
Definition: _xpoly.cxx:357
std::unique_ptr< Point[]> pPointAry
Definition: xpolyimp.hxx:32
double getMinY() const
sal_uInt16 nPoints
Definition: xpolyimp.hxx:39
void Remove(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:381
void Distort(const tools::Rectangle &rRefRect, const XPolygon &rDistortedRect)
Distort a polygon by scaling its coordinates relative to a reference rectangle into an arbitrary rect...
Definition: _xpoly.cxx:771
tools::Rectangle & Union(const tools::Rectangle &rRect)
ImpXPolygon(sal_uInt16 nInitSize, sal_uInt16 nResize=16)
Definition: _xpoly.cxx:41
o3tl::cow_wrapper< ImpXPolygon > pImpXPolygon
Definition: xpoly.hxx:46
bool IsControl(sal_uInt16 nPos) const
short path to read the CONTROL flag directly (TODO: better explain what the sense behind this flag is...
Definition: _xpoly.cxx:471
void InsertSpace(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:148
void SetFlags(sal_uInt16 nPos, PolyFlags eFlags)
set the flags for the point at the given position
Definition: _xpoly.cxx:464
XPolyPolygon & operator=(const XPolyPolygon &)
bool operator==(const XPolygon &rXPoly) const
Definition: _xpoly.cxx:450
long Left() const
static bool CheckAngles(sal_uInt16 &nStart, sal_uInt16 nEnd, sal_uInt16 &nA1, sal_uInt16 &nA2)
Definition: _xpoly.cxx:592
double getMinX() const
sal_uInt16 GetSize() const
Definition: _xpoly.cxx:345
sal_uInt16 GetPointCount() const
Definition: _xpoly.cxx:351
void Remove(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:173
void Remove(sal_uInt16 nPos)
Definition: _xpoly.cxx:880
void PointsToBezier(sal_uInt16 nFirst)
convert four polygon points into a Bézier curve
Definition: _xpoly.cxx:680
XPolygon & operator=(const XPolygon &)
Point TopRight() const
tools::Rectangle GetBoundRect() const
Definition: _xpoly.cxx:900
long Y() const
sal_uInt16 nPos
void Distort(const tools::Rectangle &rRefRect, const XPolygon &rDistortedRect)
Distort a polygon by scaling its coordinates relative to a reference rectangle into an arbitrary rect...
Definition: _xpoly.cxx:933
PolyFlags
bool IsSmooth(sal_uInt16 nPos) const
short path to read the SMOOTH and SYMMTR flag directly (TODO: better explain what the sense behind th...
Definition: _xpoly.cxx:477