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