1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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)
46 ImpXPolygon::ImpXPolygon( const ImpXPolygon
& rImpXPoly
)
47 : pOldPointAry(nullptr)
48 , bDeleteOldPoints(false)
50 , nResize(rImpXPoly
.nResize
)
53 rImpXPoly
.CheckPointDelete();
55 Resize( rImpXPoly
.nSize
);
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()
66 if ( bDeleteOldPoints
)
68 delete[] pOldPointAry
;
69 pOldPointAry
= nullptr;
73 bool ImpXPolygon::operator==(const ImpXPolygon
& rImpXPoly
) const
75 return nPoints
==rImpXPoly
.nPoints
&&
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
)
94 PolyFlags
* pOldFlagAry
= pFlagAry
.release();
95 sal_uInt16 nOldSize
= nSize
;
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
109 pPointAry
.reset( new Point
[ nSize
] );
112 pFlagAry
.reset( new PolyFlags
[ nSize
] );
113 memset( pFlagAry
.get(), 0, nSize
);
119 if( nOldSize
< nSize
)
121 memcpy( pPointAry
.get(), pOldPointAry
, nOldSize
*sizeof( Point
) );
122 memcpy( pFlagAry
.get(), pOldFlagAry
, nOldSize
);
126 memcpy( pPointAry
.get(), pOldPointAry
, nSize
*sizeof( Point
) );
127 memcpy( pFlagAry
.get(), pOldFlagAry
, nSize
);
129 // adjust number of valid points
130 if( nPoints
> nSize
)
135 delete[] pOldPointAry
;
136 pOldPointAry
= nullptr;
139 bDeleteOldPoints
= true;
140 delete[] pOldFlagAry
;
143 void ImpXPolygon::InsertSpace( sal_uInt16 nPos
, sal_uInt16 nCount
)
147 if ( 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
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
)
172 if( (nPos
+ nCount
) > nPoints
)
175 sal_uInt16 nMove
= nPoints
- nPos
- nCount
;
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
)
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
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
);
243 for (sal_uInt16 nQuad
= 0; nQuad
< 4; nQuad
++)
247 case 0: aCenter
= rRect
.TopLeft();
248 aCenter
.AdjustX( -nRx
);
249 aCenter
.AdjustY(nRy
);
251 case 1: aCenter
= rRect
.TopRight();
252 aCenter
.AdjustX(nRx
);
253 aCenter
.AdjustY(nRy
);
255 case 2: aCenter
= rRect
.BottomRight();
256 aCenter
.AdjustX(nRx
);
257 aCenter
.AdjustY( -nRy
);
259 case 3: aCenter
= rRect
.BottomLeft();
260 aCenter
.AdjustX( -nRx
);
261 aCenter
.AdjustY( -nRy
);
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
;
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
)
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
);
294 bool bLoopEnd
= false;
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
);
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
;
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
;
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(),
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
)
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
)
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()));
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
;
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
);
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
);
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
;
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
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
;
625 Point
* pPoints
= pImpXPolygon
->pPointAry
.get();
626 Point aDiff
= pPoints
[nDrag
] - pPoints
[nCenter
];
627 double fDiv
= CalcDistance(nCenter
, nDrag
);
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
);
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;
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) )
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 )
702 if ( nPart2Length
== nFullLength
)
704 if ( nPart1Length
== nFullLength
)
705 nPart1Length
= nPart2Length
- 1;
706 if ( nPart1Length
<= 0 )
708 if ( nPart2Length
<= 0 || nPart2Length
== nPart1Length
)
709 nPart2Length
= nPart1Length
+ 1;
711 fT1
= nPart1Length
/ nFullLength
;
713 fT2
= nPart2Length
/ nFullLength
;
715 fV
= 3 * (1.0 - (fT1
* fU2
) / (fT2
* fU1
));
717 fX1
= fTx1
/ (fT1
* fU1
* fU1
) - fTx2
* fT1
/ (fT2
* fT2
* fU1
* fU2
);
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
);
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:
763 * 2: bottom right 3----2
766 void XPolygon::Distort(const tools::Rectangle
& rRefRect
,
767 const XPolygon
& rDistortedRect
)
769 pImpXPolygon
->CheckPointDelete();
774 Xr
= rRefRect
.Left();
776 Wr
= rRefRect
.GetWidth();
777 Hr
= rRefRect
.GetHeight();
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
;
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
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
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
);
844 XPolyPolygon::XPolyPolygon() = default;
846 XPolyPolygon::XPolyPolygon( const XPolyPolygon
& ) = default;
848 XPolyPolygon::XPolyPolygon( XPolyPolygon
&& ) = default;
850 XPolyPolygon::XPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPolygon
)
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() );
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:
925 * 2: bottom right 3----2
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());
948 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */