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 <tools/debug.hxx>
25 #include <tools/poly.hxx>
26 #include <tools/helpers.hxx>
27 #include <tools/gen.hxx>
28 
29 #include <svx/xpoly.hxx>
30 #include <xpolyimp.hxx>
34 
35 
36 ImpXPolygon::ImpXPolygon(sal_uInt16 nInitSize, sal_uInt16 _nResize)
37  : pOldPointAry(nullptr)
38  , bDeleteOldPoints(false)
39  , nSize(0)
40  , nResize(_nResize)
41  , nPoints(0)
42 {
43  Resize(nInitSize);
44 }
45 
47  : pOldPointAry(nullptr)
48  , bDeleteOldPoints(false)
49  , nSize(0)
50  , nResize(rImpXPoly.nResize)
51  , nPoints(0)
52 {
53  rImpXPoly.CheckPointDelete();
54 
55  Resize( rImpXPoly.nSize );
56 
57  // copy
58  nPoints = rImpXPoly.nPoints;
59  memcpy( pPointAry.get(), rImpXPoly.pPointAry.get(), nSize*sizeof( Point ) );
60  memcpy( pFlagAry.get(), rImpXPoly.pFlagAry.get(), nSize );
61 }
62 
64 {
65  pPointAry.reset();
66  if ( bDeleteOldPoints )
67  {
68  delete[] pOldPointAry;
69  pOldPointAry = nullptr;
70  }
71 }
72 
73 bool ImpXPolygon::operator==(const ImpXPolygon& rImpXPoly) const
74 {
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));
79 }
80 
89 void ImpXPolygon::Resize( sal_uInt16 nNewSize, bool bDeletePoints )
90 {
91  if( nNewSize == nSize )
92  return;
93 
94  PolyFlags* pOldFlagAry = pFlagAry.release();
95  sal_uInt16 nOldSize = nSize;
96 
98  pOldPointAry = pPointAry.release();
99 
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 )
103  {
104  DBG_ASSERT(nResize, "Trying to resize but nResize = 0 !");
105  nNewSize = nSize + ((nNewSize-nSize-1) / nResize + 1) * nResize;
106  }
107  // create point array
108  nSize = nNewSize;
109  pPointAry.reset( new Point[ nSize ] );
110 
111  // create flag array
112  pFlagAry.reset( new PolyFlags[ nSize ] );
113  memset( pFlagAry.get(), 0, nSize );
114 
115  // copy if needed
116  if( !nOldSize )
117  return;
118 
119  if( nOldSize < nSize )
120  {
121  memcpy( pPointAry.get(), pOldPointAry, nOldSize*sizeof( Point ) );
122  memcpy( pFlagAry.get(), pOldFlagAry, nOldSize );
123  }
124  else
125  {
126  memcpy( pPointAry.get(), pOldPointAry, nSize*sizeof( Point ) );
127  memcpy( pFlagAry.get(), pOldFlagAry, nSize );
128 
129  // adjust number of valid points
130  if( nPoints > nSize )
131  nPoints = nSize;
132  }
133  if ( bDeletePoints )
134  {
135  delete[] pOldPointAry;
136  pOldPointAry = nullptr;
137  }
138  else
139  bDeleteOldPoints = true;
140  delete[] pOldFlagAry;
141 }
142 
143 void ImpXPolygon::InsertSpace( sal_uInt16 nPos, sal_uInt16 nCount )
144 {
146 
147  if ( nPos > nPoints )
148  nPos = nPoints;
149 
150  // if the polygon is too small then enlarge it
151  if( (nPoints + nCount) > nSize )
152  Resize( nPoints + nCount );
153 
154  // If the insert is not at the last position, move everything after backwards
155  if( nPos < nPoints )
156  {
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 );
161  }
162  std::fill(pPointAry.get() + nPos, pPointAry.get() + nPos + nCount, Point());
163  memset( &pFlagAry [nPos], 0, nCount );
164 
165  nPoints = nPoints + nCount;
166 }
167 
168 void ImpXPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
169 {
171 
172  if( (nPos + nCount) > nPoints )
173  return;
174 
175  sal_uInt16 nMove = nPoints - nPos - nCount;
176 
177  if( nMove )
178  {
179  memmove( &pPointAry[nPos], &pPointAry[nPos+nCount],
180  nMove * sizeof(Point) );
181  memmove( &pFlagAry[nPos], &pFlagAry[nPos+nCount], nMove );
182  }
183  std::fill(pPointAry.get() + (nPoints - nCount), pPointAry.get() + nPoints, Point());
184  memset( &pFlagAry [nPoints - nCount], 0, nCount );
185  nPoints = nPoints - nCount;
186 }
187 
189 {
190  if ( bDeleteOldPoints )
191  {
192  delete[] pOldPointAry;
193  const_cast< ImpXPolygon* >(this)->pOldPointAry = nullptr;
194  const_cast< ImpXPolygon* >(this)->bDeleteOldPoints = false;
195  }
196 }
197 
198 XPolygon::XPolygon( sal_uInt16 nSize )
199  : pImpXPolygon( ImpXPolygon( nSize, 16 ) )
200 {
201 }
202 
203 XPolygon::XPolygon( const XPolygon& ) = default;
204 
205 XPolygon::XPolygon( XPolygon&& ) = default;
206 
209  : pImpXPolygon( rPoly.GetSize() )
210 {
211  sal_uInt16 nSize = rPoly.GetSize();
212  pImpXPolygon->nPoints = nSize;
213 
214  for( sal_uInt16 i = 0; i < nSize; i++ )
215  {
216  pImpXPolygon->pPointAry[i] = rPoly[i];
217  pImpXPolygon->pFlagAry[i] = rPoly.GetFlags( i );
218  }
219 }
220 
222 XPolygon::XPolygon(const tools::Rectangle& rRect, long nRx, long nRy)
223  : pImpXPolygon( 17 )
224 {
225  long nWh = (rRect.GetWidth() - 1) / 2;
226  long nHh = (rRect.GetHeight() - 1) / 2;
227 
228  if ( nRx > nWh ) nRx = nWh;
229  if ( nRy > nHh ) nRy = nHh;
230 
231  // negate Rx => circle clockwise
232  nRx = -nRx;
233 
234  // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
235  long nXHdl = static_cast<long>(0.552284749 * nRx);
236  long nYHdl = static_cast<long>(0.552284749 * nRy);
237  sal_uInt16 nPos = 0;
238 
239  if ( nRx && nRy )
240  {
241  Point aCenter;
242 
243  for (sal_uInt16 nQuad = 0; nQuad < 4; nQuad++)
244  {
245  switch ( nQuad )
246  {
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;
263  }
264  GenBezArc(aCenter, nRx, nRy, nXHdl, nYHdl, 0, 900, nQuad, nPos);
265  pImpXPolygon->pFlagAry[nPos ] = PolyFlags::Smooth;
266  pImpXPolygon->pFlagAry[nPos+3] = PolyFlags::Smooth;
267  nPos += 4;
268  }
269  }
270  else
271  {
272  pImpXPolygon->pPointAry[nPos++] = rRect.TopLeft();
273  pImpXPolygon->pPointAry[nPos++] = rRect.TopRight();
274  pImpXPolygon->pPointAry[nPos++] = rRect.BottomRight();
275  pImpXPolygon->pPointAry[nPos++] = rRect.BottomLeft();
276  }
278  pImpXPolygon->nPoints = nPos + 1;
279 }
280 
282 XPolygon::XPolygon(const Point& rCenter, long nRx, long nRy,
283  sal_uInt16 nStartAngle, sal_uInt16 nEndAngle, bool bClose)
284  : pImpXPolygon( 17 )
285 {
286  nStartAngle %= 3600;
287  if ( nEndAngle > 3600 ) nEndAngle %= 3600;
288  bool bFull = (nStartAngle == 0 && nEndAngle == 3600);
289 
290  // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
291  long nXHdl = static_cast<long>(0.552284749 * nRx);
292  long nYHdl = static_cast<long>(0.552284749 * nRy);
293  sal_uInt16 nPos = 0;
294  bool bLoopEnd = false;
295 
296  do
297  {
298  sal_uInt16 nA1, nA2;
299  sal_uInt16 nQuad = nStartAngle / 900;
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;
306 
307  } while ( !bLoopEnd );
308 
309  // if not a full circle then connect edges with center point if necessary
310  if ( !bFull && bClose )
311  pImpXPolygon->pPointAry[++nPos] = rCenter;
312 
313  if ( bFull )
314  {
315  pImpXPolygon->pFlagAry[0 ] = PolyFlags::Smooth;
316  pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
317  }
318  pImpXPolygon->nPoints = nPos + 1;
319 }
320 
321 XPolygon::~XPolygon() = default;
322 
323 void XPolygon::SetPointCount( sal_uInt16 nPoints )
324 {
326 
327  if( pImpXPolygon->nSize < nPoints )
328  pImpXPolygon->Resize( nPoints );
329 
330  if ( nPoints < pImpXPolygon->nPoints )
331  {
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 );
336  }
337  pImpXPolygon->nPoints = nPoints;
338 }
339 
340 sal_uInt16 XPolygon::GetSize() const
341 {
343  return pImpXPolygon->nSize;
344 }
345 
346 sal_uInt16 XPolygon::GetPointCount() const
347 {
349  return pImpXPolygon->nPoints;
350 }
351 
352 void XPolygon::Insert( sal_uInt16 nPos, const Point& rPt, PolyFlags eFlags )
353 {
354  if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
355  pImpXPolygon->InsertSpace( nPos, 1 );
356  pImpXPolygon->pPointAry[nPos] = rPt;
357  pImpXPolygon->pFlagAry[nPos] = eFlags;
358 }
359 
360 void XPolygon::Insert( sal_uInt16 nPos, const XPolygon& rXPoly )
361 {
362  if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
363 
364  sal_uInt16 nPoints = rXPoly.GetPointCount();
365 
366  pImpXPolygon->InsertSpace( nPos, nPoints );
367 
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 );
374 }
375 
376 void XPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
377 {
378  pImpXPolygon->Remove( nPos, nCount );
379 }
380 
381 void XPolygon::Move( long nHorzMove, long nVertMove )
382 {
383  if ( !nHorzMove && !nVertMove )
384  return;
385 
386  // move points
387  sal_uInt16 nCount = pImpXPolygon->nPoints;
388  for ( sal_uInt16 i = 0; i < nCount; i++ )
389  {
390  Point* pPt = &(pImpXPolygon->pPointAry[i]);
391  pPt->AdjustX( nHorzMove );
392  pPt->AdjustY( nVertMove );
393  }
394 }
395 
397 {
399  tools::Rectangle aRetval;
400 
401  if(pImpXPolygon->nPoints)
402  {
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.
408 
410  aRetval = tools::Rectangle(
411  FRound(aPolygonRange.getMinX()), FRound(aPolygonRange.getMinY()),
412  FRound(aPolygonRange.getMaxX()), FRound(aPolygonRange.getMaxY()));
413  }
414 
415  return aRetval;
416 }
417 
418 const Point& XPolygon::operator[]( sal_uInt16 nPos ) const
419 {
420  DBG_ASSERT(nPos < pImpXPolygon->nPoints, "Invalid index at const array access to XPolygon");
421 
423  return pImpXPolygon->pPointAry[nPos];
424 }
425 
426 Point& XPolygon::operator[]( sal_uInt16 nPos )
427 {
429 
430  if( nPos >= pImpXPolygon->nSize )
431  {
432  DBG_ASSERT(pImpXPolygon->nResize, "Invalid index at array access to XPolygon");
433  pImpXPolygon->Resize(nPos + 1, false);
434  }
435  if( nPos >= pImpXPolygon->nPoints )
436  pImpXPolygon->nPoints = nPos + 1;
437 
438  return pImpXPolygon->pPointAry[nPos];
439 }
440 
441 XPolygon& XPolygon::operator=( const XPolygon& ) = default;
442 
443 XPolygon& XPolygon::operator=( XPolygon&& ) = default;
444 
445 bool XPolygon::operator==( const XPolygon& rXPoly ) const
446 {
448  return rXPoly.pImpXPolygon == pImpXPolygon;
449 }
450 
452 PolyFlags XPolygon::GetFlags( sal_uInt16 nPos ) const
453 {
455  return pImpXPolygon->pFlagAry[nPos];
456 }
457 
459 void XPolygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags )
460 {
462  pImpXPolygon->pFlagAry[nPos] = eFlags;
463 }
464 
466 bool XPolygon::IsControl(sal_uInt16 nPos) const
467 {
468  return pImpXPolygon->pFlagAry[nPos] == PolyFlags::Control;
469 }
470 
472 bool XPolygon::IsSmooth(sal_uInt16 nPos) const
473 {
474  PolyFlags eFlag = pImpXPolygon->pFlagAry[nPos];
475  return ( eFlag == PolyFlags::Smooth || eFlag == PolyFlags::Symmetric );
476 }
477 
483 double XPolygon::CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
484 {
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);
490 }
491 
492 void XPolygon::SubdivideBezier(sal_uInt16 nPos, bool bCalcFirst, double fT)
493 {
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;
502 
503  if ( bCalcFirst )
504  {
505  nPos += 3;
506  nPosInc = -1;
507  nIdxInc = 0;
508  }
509  else
510  {
511  nPosInc = 1;
512  nIdxInc = 1;
513  }
514  pPoints[nPos].setX( static_cast<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<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<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<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<long>(fU * pPoints[nIdx ].X() +
533  fT * pPoints[nIdx+1].X()) );
534  pPoints[nPos].setY( static_cast<long>(fU * pPoints[nIdx ].Y() +
535  fT * pPoints[nIdx+1].Y()) );
536 }
537 
539 void XPolygon::GenBezArc(const Point& rCenter, long nRx, long nRy,
540  long nXHdl, long nYHdl, sal_uInt16 nStart, sal_uInt16 nEnd,
541  sal_uInt16 nQuad, sal_uInt16 nFirst)
542 {
543  Point* pPoints = pImpXPolygon->pPointAry.get();
544  pPoints[nFirst ] = rCenter;
545  pPoints[nFirst+3] = rCenter;
546 
547  if ( nQuad == 1 || nQuad == 2 )
548  {
549  nRx = -nRx; nXHdl = -nXHdl;
550  }
551  if ( nQuad == 0 || nQuad == 1 )
552  {
553  nRy = -nRy; nYHdl = -nYHdl;
554  }
555 
556  if ( nQuad == 0 || nQuad == 2 )
557  {
558  pPoints[nFirst].AdjustX( nRx );
559  pPoints[nFirst+3].AdjustY( nRy );
560  }
561  else
562  {
563  pPoints[nFirst].AdjustY( nRy );
564  pPoints[nFirst+3].AdjustX( nRx );
565  }
566  pPoints[nFirst+1] = pPoints[nFirst];
567  pPoints[nFirst+2] = pPoints[nFirst+3];
568 
569  if ( nQuad == 0 || nQuad == 2 )
570  {
571  pPoints[nFirst+1].AdjustY( nYHdl );
572  pPoints[nFirst+2].AdjustX( nXHdl );
573  }
574  else
575  {
576  pPoints[nFirst+1].AdjustX( nXHdl );
577  pPoints[nFirst+2].AdjustY( nYHdl );
578  }
579  if ( nStart > 0 )
580  SubdivideBezier(nFirst, false, static_cast<double>(nStart) / 900);
581  if ( nEnd < 900 )
582  SubdivideBezier(nFirst, true, static_cast<double>(nEnd-nStart) / (900-nStart));
583  SetFlags(nFirst+1, PolyFlags::Control);
584  SetFlags(nFirst+2, PolyFlags::Control);
585 }
586 
587 bool XPolygon::CheckAngles(sal_uInt16& nStart, sal_uInt16 nEnd, sal_uInt16& nA1, sal_uInt16& nA2)
588 {
589  if ( nStart == 3600 ) nStart = 0;
590  if ( nEnd == 0 ) nEnd = 3600;
591  sal_uInt16 nStPrev = nStart;
592  sal_uInt16 nMax = (nStart / 900 + 1) * 900;
593  sal_uInt16 nMin = nMax - 900;
594 
595  if ( nEnd >= nMax || nEnd <= nStart ) nA2 = 900;
596  else nA2 = nEnd - nMin;
597  nA1 = nStart - nMin;
598  nStart = nMax;
599 
600  // returns true when the last segment was calculated
601  return (nStPrev < nEnd && nStart >= nEnd);
602 }
603 
615 void XPolygon::CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
616 {
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) )
620  {
621  sal_uInt16 nTmp = nDrag;
622  nDrag = nPnt;
623  nPnt = nTmp;
624  }
625  Point* pPoints = pImpXPolygon->pPointAry.get();
626  Point aDiff = pPoints[nDrag] - pPoints[nCenter];
627  double fDiv = CalcDistance(nCenter, nDrag);
628 
629  if ( fDiv )
630  {
631  double fRatio = CalcDistance(nCenter, nPnt) / fDiv;
632  // keep the length if SMOOTH
633  if ( GetFlags(nCenter) == PolyFlags::Smooth || !IsControl(nDrag) )
634  {
635  aDiff.setX( static_cast<long>(fRatio * aDiff.X()) );
636  aDiff.setY( static_cast<long>(fRatio * aDiff.Y()) );
637  }
638  pPoints[nPnt] = pPoints[nCenter] - aDiff;
639  }
640 }
641 
648 void XPolygon::CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
649 {
650  double fAbsLen = CalcDistance(nNext, nPrev);
651 
652  if ( !fAbsLen )
653  return;
654 
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;
661 
662  // same length for both sides if SYMMTR
663  if ( GetFlags(nCenter) == PolyFlags::Symmetric )
664  {
665  fPrevLen = (fNextLen + fPrevLen) / 2;
666  fNextLen = fPrevLen;
667  }
668  rNext.setX( rCenter.X() + static_cast<long>(fNextLen * aDiff.X()) );
669  rNext.setY( rCenter.Y() + static_cast<long>(fNextLen * aDiff.Y()) );
670  rPrev.setX( rCenter.X() - static_cast<long>(fPrevLen * aDiff.X()) );
671  rPrev.setY( rCenter.Y() - static_cast<long>(fPrevLen * aDiff.Y()) );
672 }
673 
675 void XPolygon::PointsToBezier(sal_uInt16 nFirst)
676 {
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();
682 
683  if ( nFirst > pImpXPolygon->nPoints - 4 || IsControl(nFirst) ||
684  IsControl(nFirst+1) || IsControl(nFirst+2) || IsControl(nFirst+3) )
685  return;
686 
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();
695 
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;
701 
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;
710 
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));
716 
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;
721 
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;
726 
727  fX2 = fTx2 / (fT2 * fT2 * fU2 * 3) - fX0 * fU2 * fU2 / ( fT2 * fT2 * 3);
728  fX2 -= fX1 * fU2 / fT2;
729  fX2 -= fX3 * fT2 / (fU2 * 3);
730 
731  fY2 = fTy2 / (fT2 * fT2 * fU2 * 3) - fY0 * fU2 * fU2 / ( fT2 * fT2 * 3);
732  fY2 -= fY1 * fU2 / fT2;
733  fY2 -= fY3 * fT2 / (fU2 * 3);
734 
735  pPoints[nFirst+1] = Point(static_cast<long>(fX1), static_cast<long>(fY1));
736  pPoints[nFirst+2] = Point(static_cast<long>(fX2), static_cast<long>(fY2));
737  SetFlags(nFirst+1, PolyFlags::Control);
738  SetFlags(nFirst+2, PolyFlags::Control);
739 }
740 
742 void XPolygon::Scale(double fSx, double fSy)
743 {
745 
746  sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
747 
748  for (sal_uInt16 i = 0; i < nPntCnt; i++)
749  {
750  Point& rPnt = pImpXPolygon->pPointAry[i];
751  rPnt.setX( static_cast<long>(fSx * rPnt.X()) );
752  rPnt.setY( static_cast<long>(fSy * rPnt.Y()) );
753  }
754 }
755 
766 void XPolygon::Distort(const tools::Rectangle& rRefRect,
767  const XPolygon& rDistortedRect)
768 {
770 
771  long Xr, Wr;
772  long Yr, Hr;
773 
774  Xr = rRefRect.Left();
775  Yr = rRefRect.Top();
776  Wr = rRefRect.GetWidth();
777  Hr = rRefRect.GetHeight();
778 
779  if ( !Wr || !Hr )
780  return;
781 
782  long X1, X2, X3, X4;
783  long Y1, Y2, Y3, Y4;
784  DBG_ASSERT(rDistortedRect.pImpXPolygon->nPoints >= 4,
785  "Distort: rectangle too small");
786 
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();
795 
796  sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
797 
798  for (sal_uInt16 i = 0; i < nPntCnt; i++)
799  {
800  double fTx, fTy, fUx, fUy;
801  Point& rPnt = pImpXPolygon->pPointAry[i];
802 
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;
807 
808  rPnt.setX( static_cast<long>( fUy * (fUx * X1 + fTx * X2) +
809  fTy * (fUx * X3 + fTx * X4) ) );
810  rPnt.setY( static_cast<long>( fUx * (fUy * Y1 + fTy * Y3) +
811  fTx * (fUy * Y2 + fTy * Y4) ) );
812  }
813 }
814 
816 {
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());
821 
822  return aSource.getB2DPolygon();
823 }
824 
826  : pImpXPolygon( tools::Polygon( rPolygon ).GetSize() )
827 {
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
831 
832  const tools::Polygon aSource(rPolygon);
833  sal_uInt16 nSize = aSource.GetSize();
834  pImpXPolygon->nPoints = nSize;
835 
836  for( sal_uInt16 i = 0; i < nSize; i++ )
837  {
838  pImpXPolygon->pPointAry[i] = aSource[i];
839  pImpXPolygon->pFlagAry[i] = aSource.GetFlags( i );
840  }
841 }
842 
843 // XPolyPolygon
844 XPolyPolygon::XPolyPolygon() = default;
845 
846 XPolyPolygon::XPolyPolygon( const XPolyPolygon& ) = default;
847 
849 
851  : pImpXPolyPolygon()
852 {
853  for(auto const& rCandidate : rPolyPolygon)
854  {
855  Insert(XPolygon(rCandidate));
856  }
857 }
858 
859 XPolyPolygon::~XPolyPolygon() = default;
860 
862 {
863  pImpXPolyPolygon->aXPolyList.emplace_back( std::move(rXPoly) );
864 }
865 
867 void XPolyPolygon::Insert( const XPolyPolygon& rXPolyPoly )
868 {
869  for ( size_t i = 0; i < rXPolyPoly.Count(); i++)
870  {
871  pImpXPolyPolygon->aXPolyList.emplace_back( rXPolyPoly[i] );
872  }
873 }
874 
875 void XPolyPolygon::Remove( sal_uInt16 nPos )
876 {
877  pImpXPolyPolygon->aXPolyList.erase( pImpXPolyPolygon->aXPolyList.begin() + nPos );
878 }
879 
880 const XPolygon& XPolyPolygon::GetObject( sal_uInt16 nPos ) const
881 {
882  return pImpXPolyPolygon->aXPolyList[ nPos ];
883 }
884 
886 {
887  pImpXPolyPolygon->aXPolyList.clear();
888 }
889 
890 sal_uInt16 XPolyPolygon::Count() const
891 {
892  return static_cast<sal_uInt16>(pImpXPolyPolygon->aXPolyList.size());
893 }
894 
896 {
897  size_t nXPoly = pImpXPolyPolygon->aXPolyList.size();
898  tools::Rectangle aRect;
899 
900  for ( size_t n = 0; n < nXPoly; n++ )
901  {
902  XPolygon const & rXPoly = pImpXPolyPolygon->aXPolyList[ n ];
903  aRect.Union( rXPoly.GetBoundRect() );
904  }
905 
906  return aRect;
907 }
908 
910 {
911  return pImpXPolyPolygon->aXPolyList[ nPos ];
912 }
913 
915 
917 
929  const XPolygon& rDistortedRect)
930 {
931  for (size_t i = 0; i < Count(); i++)
932  pImpXPolyPolygon->aXPolyList[ i ].Distort(rRefRect, rDistortedRect);
933 }
934 
936 {
937  basegfx::B2DPolyPolygon aRetval;
938 
939  for(sal_uInt16 a(0); a < Count(); a++)
940  {
941  const XPolygon& rPoly = (*this)[a];
942  aRetval.append(rPoly.getB2DPolygon());
943  }
944 
945  return aRetval;
946 }
947 
948 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
void Insert(XPolygon &&rXPoly)
Definition: _xpoly.cxx:861
long GetWidth() const
tools::Rectangle GetBoundRect() const
Definition: _xpoly.cxx:396
long GetHeight() const
sal_uInt16 Count() const
Definition: _xpoly.cxx:890
o3tl::cow_wrapper< ImpXPolyPolygon > pImpXPolyPolygon
Definition: xpoly.hxx:115
long FRound(double fVal)
PolyFlags GetFlags(sal_uInt16 nPos) const
get the flags for the point at the given position
Definition: _xpoly.cxx:452
Point BottomLeft() const
sal_Int64 n
basegfx::B2DPolyPolygon getB2DPolyPolygon() const
Definition: _xpoly.cxx:935
void SetPointCount(sal_uInt16 nPoints)
Definition: _xpoly.cxx:323
~ImpXPolygon()
Definition: _xpoly.cxx:63
const XPolygon & operator[](sal_uInt16 nPos) const
Definition: xpoly.hxx:134
double getMaxX() const
void CheckPointDelete() const
Definition: _xpoly.cxx:188
bool bDeleteOldPoints
Definition: xpolyimp.hxx:36
::std::vector< XPolygon > aXPolyList
Definition: xpolyimp.hxx:57
void Move(long nHorzMove, long nVertMove)
Definition: _xpoly.cxx:381
const XPolygon & GetObject(sal_uInt16 nPos) const
Definition: _xpoly.cxx:880
XPolygon(sal_uInt16 nSize=16)
Definition: _xpoly.cxx:198
#define X
sal_uInt16 nResize
Definition: xpolyimp.hxx:38
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:648
int nCount
bool operator==(const ImpXPolygon &rImpXPoly) const
Definition: _xpoly.cxx:73
void CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
Calculate a smooth transition to connect two Bézier curves.
Definition: _xpoly.cxx:615
long Top() const
sal_uInt16 nSize
Definition: xpolyimp.hxx:37
double getMaxY() const
const Point & operator[](sal_uInt16 nPos) const
Definition: _xpoly.cxx:418
Point * pOldPointAry
Definition: xpolyimp.hxx:35
Point BottomRight() const
#define DBG_ASSERT(sCon, aError)
int i
uno_Any a
void Clear()
Definition: _xpoly.cxx:885
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:539
std::unique_ptr< PolyFlags[]> pFlagAry
Definition: xpolyimp.hxx:34
void SubdivideBezier(sal_uInt16 nPos, bool bCalcFirst, double fT)
Definition: _xpoly.cxx:492
basegfx::B2DPolygon getB2DPolygon() const
Definition: _xpoly.cxx:815
void Scale(double fSx, double fSy)
scale in X- and/or Y-direction
Definition: _xpoly.cxx:742
double CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
calculate the euclidean distance between two points
Definition: _xpoly.cxx:483
void Resize(sal_uInt16 nNewSize, bool bDeletePoints=true)
Change polygon size.
Definition: _xpoly.cxx:89
sal_uInt16 GetSize() const
::basegfx::B2DPolygon getB2DPolygon() 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:352
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:376
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:766
tools::Rectangle & Union(const tools::Rectangle &rRect)
ImpXPolygon(sal_uInt16 nInitSize, sal_uInt16 nResize=16)
Definition: _xpoly.cxx:36
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:466
void InsertSpace(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:143
void SetFlags(sal_uInt16 nPos, PolyFlags eFlags)
set the flags for the point at the given position
Definition: _xpoly.cxx:459
XPolyPolygon & operator=(const XPolyPolygon &)
bool operator==(const XPolygon &rXPoly) const
Definition: _xpoly.cxx:445
long Left() const
static bool CheckAngles(sal_uInt16 &nStart, sal_uInt16 nEnd, sal_uInt16 &nA1, sal_uInt16 &nA2)
Definition: _xpoly.cxx:587
double getMinX() const
sal_uInt16 GetSize() const
Definition: _xpoly.cxx:340
sal_uInt16 GetPointCount() const
Definition: _xpoly.cxx:346
void Remove(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:168
void Remove(sal_uInt16 nPos)
Definition: _xpoly.cxx:875
void PointsToBezier(sal_uInt16 nFirst)
convert four polygon points into a Bézier curve
Definition: _xpoly.cxx:675
XPolygon & operator=(const XPolygon &)
Point TopRight() const
tools::Rectangle GetBoundRect() const
Definition: _xpoly.cxx:895
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:928
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:472