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 <osl/diagnose.h>
22 #include <sal/log.hxx>
23 #include <tools/bigint.hxx>
24 #include <tools/debug.hxx>
25 #include <tools/helpers.hxx>
26 #include <tools/stream.hxx>
27 #include <tools/vcompat.hxx>
28 #include <tools/gen.hxx>
30 #include <tools/line.hxx>
31 #include <tools/vector2d.hxx>
32 #include <tools/poly.hxx>
33 #include <basegfx/polygon/b2dpolygon.hxx>
34 #include <basegfx/point/b2dpoint.hxx>
35 #include <basegfx/vector/b2dvector.hxx>
36 #include <basegfx/polygon/b2dpolygontools.hxx>
37 #include <basegfx/curve/b2dcubicbezier.hxx>
51 #define EDGE_HORZ (EDGE_RIGHT | EDGE_LEFT)
52 #define EDGE_VERT (EDGE_TOP | EDGE_BOTTOM)
53 #define SMALL_DVALUE 0.0000001
54 #define FSQRT2 1.4142135623730950488016887242097
56 static ImplPolygonData aStaticImplPolygon
=
61 ImplPolygon::ImplPolygon( sal_uInt16 nInitSize
, bool bFlags
)
65 mpPointAry
= reinterpret_cast<Point
*>(new char[(sal_uIntPtr
)nInitSize
*sizeof(Point
)]);
66 memset( mpPointAry
, 0, (sal_uIntPtr
)nInitSize
*sizeof(Point
) );
73 mpFlagAry
= new sal_uInt8
[ nInitSize
];
74 memset( mpFlagAry
, 0, nInitSize
);
83 ImplPolygon::ImplPolygon( const ImplPolygon
& rImpPoly
)
85 if ( rImpPoly
.mnPoints
)
87 mpPointAry
= reinterpret_cast<Point
*>(new char[(sal_uIntPtr
)rImpPoly
.mnPoints
*sizeof(Point
)]);
88 memcpy( mpPointAry
, rImpPoly
.mpPointAry
, (sal_uIntPtr
)rImpPoly
.mnPoints
*sizeof(Point
) );
90 if( rImpPoly
.mpFlagAry
)
92 mpFlagAry
= new sal_uInt8
[ rImpPoly
.mnPoints
];
93 memcpy( mpFlagAry
, rImpPoly
.mpFlagAry
, rImpPoly
.mnPoints
);
105 mnPoints
= rImpPoly
.mnPoints
;
108 ImplPolygon::ImplPolygon( sal_uInt16 nInitSize
, const Point
* pInitAry
, const sal_uInt8
* pInitFlags
)
112 mpPointAry
= reinterpret_cast<Point
*>(new char[(sal_uIntPtr
)nInitSize
*sizeof(Point
)]);
113 memcpy( mpPointAry
, pInitAry
, (sal_uIntPtr
)nInitSize
*sizeof( Point
) );
117 mpFlagAry
= new sal_uInt8
[ nInitSize
];
118 memcpy( mpFlagAry
, pInitFlags
, nInitSize
);
130 mnPoints
= nInitSize
;
133 ImplPolygon::~ImplPolygon()
137 delete[] reinterpret_cast<char*>(mpPointAry
);
144 void ImplPolygon::ImplSetSize( sal_uInt16 nNewSize
, bool bResize
)
146 if( mnPoints
== nNewSize
)
153 pNewAry
= reinterpret_cast<Point
*>(new char[(sal_uIntPtr
)nNewSize
*sizeof(Point
)]);
157 // Copy the old points
158 if ( mnPoints
< nNewSize
)
160 // New points initialized to zero
161 memset( pNewAry
+mnPoints
, 0, (sal_uIntPtr
)(nNewSize
-mnPoints
)*sizeof(Point
) );
163 memcpy( pNewAry
, mpPointAry
, mnPoints
*sizeof(Point
) );
168 memcpy( pNewAry
, mpPointAry
, (sal_uIntPtr
)nNewSize
*sizeof(Point
) );
176 delete[] reinterpret_cast<char*>(mpPointAry
);
178 // ggf. FlagArray beruecksichtigen
181 sal_uInt8
* pNewFlagAry
;
185 pNewFlagAry
= new sal_uInt8
[ nNewSize
];
189 // copy the old flags
190 if ( mnPoints
< nNewSize
)
192 // initialize new flags to zero
193 memset( pNewFlagAry
+mnPoints
, 0, nNewSize
-mnPoints
);
194 memcpy( pNewFlagAry
, mpFlagAry
, mnPoints
);
197 memcpy( pNewFlagAry
, mpFlagAry
, nNewSize
);
204 mpFlagAry
= pNewFlagAry
;
207 mpPointAry
= pNewAry
;
211 void ImplPolygon::ImplSplit( sal_uInt16 nPos
, sal_uInt16 nSpace
, ImplPolygon
* pInitPoly
)
213 const sal_uIntPtr nSpaceSize
= nSpace
* sizeof( Point
);
215 //Can't fit this in :-(, throw ?
216 if (mnPoints
+ nSpace
> USHRT_MAX
)
219 const sal_uInt16 nNewSize
= mnPoints
+ nSpace
;
221 if( nPos
>= mnPoints
)
223 // Append at the back
225 ImplSetSize( nNewSize
, true );
229 memcpy( mpPointAry
+ nPos
, pInitPoly
->mpPointAry
, nSpaceSize
);
231 if( pInitPoly
->mpFlagAry
)
232 memcpy( mpFlagAry
+ nPos
, pInitPoly
->mpFlagAry
, nSpace
);
237 const sal_uInt16 nSecPos
= nPos
+ nSpace
;
238 const sal_uInt16 nRest
= mnPoints
- nPos
;
240 Point
* pNewAry
= reinterpret_cast<Point
*>(new char[ (sal_uIntPtr
) nNewSize
* sizeof( Point
) ]);
242 memcpy( pNewAry
, mpPointAry
, nPos
* sizeof( Point
) );
245 memcpy( pNewAry
+ nPos
, pInitPoly
->mpPointAry
, nSpaceSize
);
247 memset( pNewAry
+ nPos
, 0, nSpaceSize
);
249 memcpy( pNewAry
+ nSecPos
, mpPointAry
+ nPos
, nRest
* sizeof( Point
) );
250 delete[] reinterpret_cast<char*>(mpPointAry
);
252 // consider FlagArray
255 sal_uInt8
* pNewFlagAry
= new sal_uInt8
[ nNewSize
];
257 memcpy( pNewFlagAry
, mpFlagAry
, nPos
);
259 if( pInitPoly
&& pInitPoly
->mpFlagAry
)
260 memcpy( pNewFlagAry
+ nPos
, pInitPoly
->mpFlagAry
, nSpace
);
262 memset( pNewFlagAry
+ nPos
, 0, nSpace
);
264 memcpy( pNewFlagAry
+ nSecPos
, mpFlagAry
+ nPos
, nRest
);
266 mpFlagAry
= pNewFlagAry
;
269 mpPointAry
= pNewAry
;
274 void ImplPolygon::ImplCreateFlagArray()
278 mpFlagAry
= new sal_uInt8
[ mnPoints
];
279 memset( mpFlagAry
, 0, mnPoints
);
284 Polygon
Polygon::SubdivideBezier( const Polygon
& rPoly
)
288 // #100127# Use adaptive subdivide instead of fixed 25 segments
289 rPoly
.AdaptiveSubdivide( aPoly
);
295 inline void Polygon::ImplMakeUnique()
297 // copy references if any exist
298 if ( mpImplPolygon
->mnRefCount
!= 1 )
300 if ( mpImplPolygon
->mnRefCount
)
301 mpImplPolygon
->mnRefCount
--;
302 mpImplPolygon
= new ImplPolygon( *mpImplPolygon
);
306 inline double ImplGetParameter( const Point
& rCenter
, const Point
& rPt
, double fWR
, double fHR
)
308 const long nDX
= rPt
.X() - rCenter
.X();
309 double fAngle
= atan2( -rPt
.Y() + rCenter
.Y(), ( ( nDX
== 0L ) ? 0.000000001 : nDX
) );
311 return atan2(fWR
*sin(fAngle
), fHR
*cos(fAngle
));
316 mpImplPolygon
= static_cast<ImplPolygon
*>(&aStaticImplPolygon
);
319 Polygon::Polygon( sal_uInt16 nSize
)
323 mpImplPolygon
= new ImplPolygon( nSize
);
325 mpImplPolygon
= static_cast<ImplPolygon
*>(&aStaticImplPolygon
);
328 Polygon::Polygon( sal_uInt16 nPoints
, const Point
* pPtAry
, const sal_uInt8
* pFlagAry
)
332 mpImplPolygon
= new ImplPolygon( nPoints
, pPtAry
, pFlagAry
);
334 mpImplPolygon
= static_cast<ImplPolygon
*>(&aStaticImplPolygon
);
337 Polygon::Polygon( const Polygon
& rPoly
)
339 DBG_ASSERT( rPoly
.mpImplPolygon
->mnRefCount
< 0xFFFFFFFE, "Polygon: RefCount overflow" );
341 mpImplPolygon
= rPoly
.mpImplPolygon
;
342 if ( mpImplPolygon
->mnRefCount
)
343 mpImplPolygon
->mnRefCount
++;
346 Polygon::Polygon( const Rectangle
& rRect
)
349 if ( rRect
.IsEmpty() )
350 mpImplPolygon
= static_cast<ImplPolygon
*>(&aStaticImplPolygon
);
353 mpImplPolygon
= new ImplPolygon( 5 );
354 mpImplPolygon
->mpPointAry
[0] = rRect
.TopLeft();
355 mpImplPolygon
->mpPointAry
[1] = rRect
.TopRight();
356 mpImplPolygon
->mpPointAry
[2] = rRect
.BottomRight();
357 mpImplPolygon
->mpPointAry
[3] = rRect
.BottomLeft();
358 mpImplPolygon
->mpPointAry
[4] = rRect
.TopLeft();
362 Polygon::Polygon( const Rectangle
& rRect
, sal_uIntPtr nHorzRound
, sal_uIntPtr nVertRound
)
364 if ( rRect
.IsEmpty() )
365 mpImplPolygon
= static_cast<ImplPolygon
*>(&aStaticImplPolygon
);
368 Rectangle
aRect( rRect
);
369 aRect
.Justify(); // SJ: i9140
371 nHorzRound
= std::min( nHorzRound
, (sal_uIntPtr
) labs( aRect
.GetWidth() >> 1 ) );
372 nVertRound
= std::min( nVertRound
, (sal_uIntPtr
) labs( aRect
.GetHeight() >> 1 ) );
374 if( !nHorzRound
&& !nVertRound
)
376 mpImplPolygon
= new ImplPolygon( 5 );
377 mpImplPolygon
->mpPointAry
[0] = aRect
.TopLeft();
378 mpImplPolygon
->mpPointAry
[1] = aRect
.TopRight();
379 mpImplPolygon
->mpPointAry
[2] = aRect
.BottomRight();
380 mpImplPolygon
->mpPointAry
[3] = aRect
.BottomLeft();
381 mpImplPolygon
->mpPointAry
[4] = aRect
.TopLeft();
385 const Point
aTL( aRect
.Left() + nHorzRound
, aRect
.Top() + nVertRound
);
386 const Point
aTR( aRect
.Right() - nHorzRound
, aRect
.Top() + nVertRound
);
387 const Point
aBR( aRect
.Right() - nHorzRound
, aRect
.Bottom() - nVertRound
);
388 const Point
aBL( aRect
.Left() + nHorzRound
, aRect
.Bottom() - nVertRound
);
389 Polygon
* pEllipsePoly
= new Polygon( Point(), nHorzRound
, nVertRound
);
390 sal_uInt16 i
, nEnd
, nSize4
= pEllipsePoly
->GetSize() >> 2;
392 mpImplPolygon
= new ImplPolygon( pEllipsePoly
->GetSize() + 1 );
394 const Point
* pSrcAry
= pEllipsePoly
->GetConstPointAry();
395 Point
* pDstAry
= mpImplPolygon
->mpPointAry
;
397 for( i
= 0, nEnd
= nSize4
; i
< nEnd
; i
++ )
398 ( pDstAry
[ i
] = pSrcAry
[ i
] ) += aTR
;
400 for( nEnd
= nEnd
+ nSize4
; i
< nEnd
; i
++ )
401 ( pDstAry
[ i
] = pSrcAry
[ i
] ) += aTL
;
403 for( nEnd
= nEnd
+ nSize4
; i
< nEnd
; i
++ )
404 ( pDstAry
[ i
] = pSrcAry
[ i
] ) += aBL
;
406 for( nEnd
= nEnd
+ nSize4
; i
< nEnd
; i
++ )
407 ( pDstAry
[ i
] = pSrcAry
[ i
] ) += aBR
;
409 pDstAry
[ nEnd
] = pDstAry
[ 0 ];
415 Polygon::Polygon( const Point
& rCenter
, long nRadX
, long nRadY
, sal_uInt16 nPoints
)
419 // Compute default (depends on size)
422 nPoints
= (sal_uInt16
) MinMax(
423 ( F_PI
* ( 1.5 * ( nRadX
+ nRadY
) -
424 sqrt( (double) labs( nRadX
* nRadY
) ) ) ),
427 if( ( nRadX
> 32 ) && ( nRadY
> 32 ) && ( nRadX
+ nRadY
) < 8192 )
431 // Ceil number of points until divisible by four
432 mpImplPolygon
= new ImplPolygon( nPoints
= (nPoints
+ 3) & ~3 );
436 sal_uInt16 nPoints2
= nPoints
>> 1;
437 sal_uInt16 nPoints4
= nPoints
>> 2;
439 double nAngleStep
= F_PI2
/ ( nPoints4
- 1 );
441 for( i
=0, nAngle
= 0.0; i
< nPoints4
; i
++, nAngle
+= nAngleStep
)
443 long nX
= FRound( nRadX
* cos( nAngle
) );
444 long nY
= FRound( -nRadY
* sin( nAngle
) );
446 pPt
= &(mpImplPolygon
->mpPointAry
[i
]);
447 pPt
->X() = nX
+ rCenter
.X();
448 pPt
->Y() = nY
+ rCenter
.Y();
449 pPt
= &(mpImplPolygon
->mpPointAry
[nPoints2
-i
-1]);
450 pPt
->X() = -nX
+ rCenter
.X();
451 pPt
->Y() = nY
+ rCenter
.Y();
452 pPt
= &(mpImplPolygon
->mpPointAry
[i
+nPoints2
]);
453 pPt
->X() = -nX
+ rCenter
.X();
454 pPt
->Y() = -nY
+ rCenter
.Y();
455 pPt
= &(mpImplPolygon
->mpPointAry
[nPoints
-i
-1]);
456 pPt
->X() = nX
+ rCenter
.X();
457 pPt
->Y() = -nY
+ rCenter
.Y();
461 mpImplPolygon
= static_cast<ImplPolygon
*>(&aStaticImplPolygon
);
464 Polygon::Polygon( const Rectangle
& rBound
, const Point
& rStart
, const Point
& rEnd
,
465 PolyStyle eStyle
, bool bFullCircle
)
467 const long nWidth
= rBound
.GetWidth();
468 const long nHeight
= rBound
.GetHeight();
470 if( ( nWidth
> 1 ) && ( nHeight
> 1 ) )
472 const Point
aCenter( rBound
.Center() );
473 const long nRadX
= aCenter
.X() - rBound
.Left();
474 const long nRadY
= aCenter
.Y() - rBound
.Top();
477 nPoints
= (sal_uInt16
) MinMax(
478 ( F_PI
* ( 1.5 * ( nRadX
+ nRadY
) -
479 sqrt( (double) labs( nRadX
* nRadY
) ) ) ),
482 if( ( nRadX
> 32 ) && ( nRadY
> 32 ) && ( nRadX
+ nRadY
) < 8192 )
486 const double fRadX
= nRadX
;
487 const double fRadY
= nRadY
;
488 const double fCenterX
= aCenter
.X();
489 const double fCenterY
= aCenter
.Y();
490 double fStart
= ImplGetParameter( aCenter
, rStart
, fRadX
, fRadY
);
491 double fEnd
= ImplGetParameter( aCenter
, rEnd
, fRadX
, fRadY
);
492 double fDiff
= fEnd
- fStart
;
503 // Proportionally shrink number of points( fDiff / (2PI) );
504 nPoints
= std::max( (sal_uInt16
) ( ( fDiff
* 0.1591549 ) * nPoints
), (sal_uInt16
) 16 );
505 fStep
= fDiff
/ ( nPoints
- 1 );
507 if( POLY_PIE
== eStyle
)
509 const Point
aCenter2( FRound( fCenterX
), FRound( fCenterY
) );
513 mpImplPolygon
= new ImplPolygon( nPoints
+ 2 );
514 mpImplPolygon
->mpPointAry
[ 0 ] = aCenter2
;
515 mpImplPolygon
->mpPointAry
[ nEnd
] = aCenter2
;
519 mpImplPolygon
= new ImplPolygon( ( POLY_CHORD
== eStyle
) ? ( nPoints
+ 1 ) : nPoints
);
524 for(; nStart
< nEnd
; nStart
++, fStart
+= fStep
)
526 Point
& rPt
= mpImplPolygon
->mpPointAry
[ nStart
];
528 rPt
.X() = FRound( fCenterX
+ fRadX
* cos( fStart
) );
529 rPt
.Y() = FRound( fCenterY
- fRadY
* sin( fStart
) );
532 if( POLY_CHORD
== eStyle
)
533 mpImplPolygon
->mpPointAry
[ nPoints
] = mpImplPolygon
->mpPointAry
[ 0 ];
536 mpImplPolygon
= static_cast<ImplPolygon
*>( &aStaticImplPolygon
);
539 Polygon::Polygon( const Point
& rBezPt1
, const Point
& rCtrlPt1
,
540 const Point
& rBezPt2
, const Point
& rCtrlPt2
,
543 nPoints
= ( 0 == nPoints
) ? 25 : ( ( nPoints
< 2 ) ? 2 : nPoints
);
545 const double fInc
= 1.0 / ( nPoints
- 1 );
546 double fK_1
= 0.0, fK1_1
= 1.0;
547 double fK_2
, fK_3
, fK1_2
, fK1_3
, fK12
, fK21
;
548 const double fX0
= rBezPt1
.X();
549 const double fY0
= rBezPt1
.Y();
550 const double fX1
= 3.0 * rCtrlPt1
.X();
551 const double fY1
= 3.0 * rCtrlPt1
.Y();
552 const double fX2
= 3.0 * rCtrlPt2
.X();
553 const double fY2
= 3.0 * rCtrlPt2
.Y();
554 const double fX3
= rBezPt2
.X();
555 const double fY3
= rBezPt2
.Y();
557 mpImplPolygon
= new ImplPolygon( nPoints
);
559 for( sal_uInt16 i
= 0; i
< nPoints
; i
++, fK_1
+= fInc
, fK1_1
-= fInc
)
561 Point
& rPt
= mpImplPolygon
->mpPointAry
[ i
];
563 fK_2
= fK_1
, fK_3
= ( fK_2
*= fK_1
), fK_3
*= fK_1
;
564 fK1_2
= fK1_1
, fK1_3
= ( fK1_2
*= fK1_1
), fK1_3
*= fK1_1
;
565 fK12
= fK_1
* fK1_2
, fK21
= fK_2
* fK1_1
;
567 rPt
.X() = FRound( fK1_3
* fX0
+ fK12
* fX1
+ fK21
* fX2
+ fK_3
* fX3
);
568 rPt
.Y() = FRound( fK1_3
* fY0
+ fK12
* fY1
+ fK21
* fY2
+ fK_3
* fY3
);
575 // Remove if refcount == 0, otherwise decrement refcount
576 if ( mpImplPolygon
->mnRefCount
)
578 if ( mpImplPolygon
->mnRefCount
> 1 )
579 mpImplPolygon
->mnRefCount
--;
581 delete mpImplPolygon
;
585 const Point
* Polygon::GetConstPointAry() const
587 return mpImplPolygon
->mpPointAry
;
590 const sal_uInt8
* Polygon::GetConstFlagAry() const
592 return mpImplPolygon
->mpFlagAry
;
595 void Polygon::SetPoint( const Point
& rPt
, sal_uInt16 nPos
)
597 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
,
598 "Polygon::SetPoint(): nPos >= nPoints" );
601 mpImplPolygon
->mpPointAry
[nPos
] = rPt
;
604 void Polygon::SetFlags( sal_uInt16 nPos
, PolyFlags eFlags
)
606 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
,
607 "Polygon::SetFlags(): nPos >= nPoints" );
609 // we do only want to create the flag array if there
610 // is at least one flag different to POLY_NORMAL
611 if ( eFlags
!= POLY_NORMAL
)
614 mpImplPolygon
->ImplCreateFlagArray();
615 mpImplPolygon
->mpFlagAry
[ nPos
] = (sal_uInt8
) eFlags
;
619 const Point
& Polygon::GetPoint( sal_uInt16 nPos
) const
621 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
,
622 "Polygon::GetPoint(): nPos >= nPoints" );
624 return mpImplPolygon
->mpPointAry
[nPos
];
627 PolyFlags
Polygon::GetFlags( sal_uInt16 nPos
) const
629 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
,
630 "Polygon::GetFlags(): nPos >= nPoints" );
631 return( mpImplPolygon
->mpFlagAry
?
632 (PolyFlags
) mpImplPolygon
->mpFlagAry
[ nPos
] :
636 bool Polygon::HasFlags() const
638 return mpImplPolygon
->mpFlagAry
!= NULL
;
641 bool Polygon::IsRect() const
643 bool bIsRect
= false;
644 if ( mpImplPolygon
->mpFlagAry
== NULL
)
646 if ( ( ( mpImplPolygon
->mnPoints
== 5 ) && ( mpImplPolygon
->mpPointAry
[ 0 ] == mpImplPolygon
->mpPointAry
[ 4 ] ) ) ||
647 ( mpImplPolygon
->mnPoints
== 4 ) )
649 if ( ( mpImplPolygon
->mpPointAry
[ 0 ].X() == mpImplPolygon
->mpPointAry
[ 3 ].X() ) &&
650 ( mpImplPolygon
->mpPointAry
[ 0 ].Y() == mpImplPolygon
->mpPointAry
[ 1 ].Y() ) &&
651 ( mpImplPolygon
->mpPointAry
[ 1 ].X() == mpImplPolygon
->mpPointAry
[ 2 ].X() ) &&
652 ( mpImplPolygon
->mpPointAry
[ 2 ].Y() == mpImplPolygon
->mpPointAry
[ 3 ].Y() ) )
659 void Polygon::SetSize( sal_uInt16 nNewSize
)
661 if( nNewSize
!= mpImplPolygon
->mnPoints
)
664 mpImplPolygon
->ImplSetSize( nNewSize
);
668 sal_uInt16
Polygon::GetSize() const
670 return mpImplPolygon
->mnPoints
;
673 void Polygon::Clear()
675 if ( mpImplPolygon
->mnRefCount
)
677 if ( mpImplPolygon
->mnRefCount
> 1 )
678 mpImplPolygon
->mnRefCount
--;
680 delete mpImplPolygon
;
683 mpImplPolygon
= static_cast<ImplPolygon
*>(&aStaticImplPolygon
);
686 double Polygon::CalcDistance( sal_uInt16 nP1
, sal_uInt16 nP2
)
688 DBG_ASSERT( nP1
< mpImplPolygon
->mnPoints
,
689 "Polygon::CalcDistance(): nPos1 >= nPoints" );
690 DBG_ASSERT( nP2
< mpImplPolygon
->mnPoints
,
691 "Polygon::CalcDistance(): nPos2 >= nPoints" );
693 const Point
& rP1
= mpImplPolygon
->mpPointAry
[ nP1
];
694 const Point
& rP2
= mpImplPolygon
->mpPointAry
[ nP2
];
695 const double fDx
= rP2
.X() - rP1
.X();
696 const double fDy
= rP2
.Y() - rP1
.Y();
698 return sqrt( fDx
* fDx
+ fDy
* fDy
);
701 void Polygon::Optimize( PolyOptimizeFlags nOptimizeFlags
, const PolyOptimizeData
* pData
)
703 DBG_ASSERT( !mpImplPolygon
->mpFlagAry
, "Optimizing could fail with beziers!" );
705 sal_uInt16 nSize
= mpImplPolygon
->mnPoints
;
707 if( bool(nOptimizeFlags
) && nSize
)
709 if( nOptimizeFlags
& PolyOptimizeFlags::EDGES
)
711 const Rectangle
aBound( GetBoundRect() );
712 const double fArea
= ( aBound
.GetWidth() + aBound
.GetHeight() ) * 0.5;
713 const sal_uInt16 nPercent
= pData
? pData
->GetPercentValue() : 50;
715 Optimize( PolyOptimizeFlags::NO_SAME
);
716 ImplReduceEdges( *this, fArea
, nPercent
);
718 else if( nOptimizeFlags
& ( PolyOptimizeFlags::REDUCE
| PolyOptimizeFlags::NO_SAME
) )
721 const Point
& rFirst
= mpImplPolygon
->mpPointAry
[ 0 ];
724 if( nOptimizeFlags
& ( PolyOptimizeFlags::REDUCE
) )
725 nReduce
= pData
? pData
->GetAbsValue() : 4UL;
729 while( nSize
&& ( mpImplPolygon
->mpPointAry
[ nSize
- 1 ] == rFirst
) )
734 sal_uInt16 nLast
= 0, nNewCount
= 1;
736 aNewPoly
.SetSize( nSize
);
737 aNewPoly
[ 0 ] = rFirst
;
739 for( sal_uInt16 i
= 1; i
< nSize
; i
++ )
741 if( ( mpImplPolygon
->mpPointAry
[ i
] != mpImplPolygon
->mpPointAry
[ nLast
] ) &&
742 ( !nReduce
|| ( nReduce
< (sal_uIntPtr
) FRound( CalcDistance( nLast
, i
) ) ) ) )
744 aNewPoly
[ nNewCount
++ ] = mpImplPolygon
->mpPointAry
[ nLast
= i
];
751 aNewPoly
.SetSize( nNewCount
);
757 nSize
= mpImplPolygon
->mnPoints
;
761 if( ( nOptimizeFlags
& PolyOptimizeFlags::CLOSE
) &&
762 ( mpImplPolygon
->mpPointAry
[ 0 ] != mpImplPolygon
->mpPointAry
[ nSize
- 1 ] ) )
764 SetSize( mpImplPolygon
->mnPoints
+ 1 );
765 mpImplPolygon
->mpPointAry
[ mpImplPolygon
->mnPoints
- 1 ] = mpImplPolygon
->mpPointAry
[ 0 ];
767 else if( ( nOptimizeFlags
& PolyOptimizeFlags::OPEN
) &&
768 ( mpImplPolygon
->mpPointAry
[ 0 ] == mpImplPolygon
->mpPointAry
[ nSize
- 1 ] ) )
770 const Point
& rFirst
= mpImplPolygon
->mpPointAry
[ 0 ];
772 while( nSize
&& ( mpImplPolygon
->mpPointAry
[ nSize
- 1 ] == rFirst
) )
782 /** Recursively subdivide cubic bezier curve via deCasteljau.
785 Output iterator, where the subdivided polylines are written to.
788 Squared difference of curve to a straight line
791 Exactly four points, interpreted as support and control points of
792 a cubic bezier curve. Must be in device coordinates, since stop
793 criterion is based on the following assumption: the device has a
794 finite resolution, it is thus sufficient to stop subdivision if the
795 curve does not deviate more than one pixel from a straight line.
798 static void ImplAdaptiveSubdivide( ::std::back_insert_iterator
< ::std::vector
< Point
> >& rPointIter
,
802 const double P1x
, const double P1y
,
803 const double P2x
, const double P2y
,
804 const double P3x
, const double P3y
,
805 const double P4x
, const double P4y
)
807 // Hard limit on recursion depth, empiric number.
808 enum {maxRecursionDepth
=128};
810 // Perform bezier flatness test (lecture notes from R. Schaback,
811 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
813 // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
816 // What is calculated here is an upper bound to the distance from
817 // a line through b_0 and b_3 (P1 and P4 in our notation) and the
818 // curve. We can drop 0 and n from the running indices, since the
819 // argument of max becomes zero for those cases.
820 const double fJ1x( P2x
- P1x
- 1.0/3.0*(P4x
- P1x
) );
821 const double fJ1y( P2y
- P1y
- 1.0/3.0*(P4y
- P1y
) );
822 const double fJ2x( P3x
- P1x
- 2.0/3.0*(P4x
- P1x
) );
823 const double fJ2y( P3y
- P1y
- 2.0/3.0*(P4y
- P1y
) );
824 const double distance2( ::std::max( fJ1x
*fJ1x
+ fJ1y
*fJ1y
,
825 fJ2x
*fJ2x
+ fJ2y
*fJ2y
) );
827 // stop if error measure does not improve anymore. This is a
828 // safety guard against floating point inaccuracies.
829 // stop at recursion level 128. This is a safety guard against
830 // floating point inaccuracies.
831 // stop if distance from line is guaranteed to be bounded by d
833 recursionDepth
< maxRecursionDepth
&&
836 // deCasteljau bezier arc, split at t=0.5
837 // Foley/vanDam, p. 508
838 const double L1x( P1x
), L1y( P1y
);
839 const double L2x( (P1x
+ P2x
)*0.5 ), L2y( (P1y
+ P2y
)*0.5 );
840 const double Hx ( (P2x
+ P3x
)*0.5 ), Hy ( (P2y
+ P3y
)*0.5 );
841 const double L3x( (L2x
+ Hx
)*0.5 ), L3y( (L2y
+ Hy
)*0.5 );
842 const double R4x( P4x
), R4y( P4y
);
843 const double R3x( (P3x
+ P4x
)*0.5 ), R3y( (P3y
+ P4y
)*0.5 );
844 const double R2x( (Hx
+ R3x
)*0.5 ), R2y( (Hy
+ R3y
)*0.5 );
845 const double R1x( (L3x
+ R2x
)*0.5 ), R1y( (L3y
+ R2y
)*0.5 );
846 const double L4x( R1x
), L4y( R1y
);
850 ImplAdaptiveSubdivide(rPointIter
, distance2
, recursionDepth
, d2
, L1x
, L1y
, L2x
, L2y
, L3x
, L3y
, L4x
, L4y
);
851 ImplAdaptiveSubdivide(rPointIter
, distance2
, recursionDepth
, d2
, R1x
, R1y
, R2x
, R2y
, R3x
, R3y
, R4x
, R4y
);
855 // requested resolution reached.
856 // Add end points to output iterator.
857 // order is preserved, since this is so to say depth first traversal.
858 *rPointIter
++ = Point( FRound(P1x
), FRound(P1y
) );
862 void Polygon::AdaptiveSubdivide( Polygon
& rResult
, const double d
) const
864 if( !mpImplPolygon
->mpFlagAry
)
871 sal_uInt16
nPts( GetSize() );
872 ::std::vector
< Point
> aPoints
;
873 aPoints
.reserve( nPts
);
874 ::std::back_insert_iterator
< ::std::vector
< Point
> > aPointIter( aPoints
);
878 if( ( i
+ 3 ) < nPts
)
880 sal_uInt8
P1( mpImplPolygon
->mpFlagAry
[ i
] );
881 sal_uInt8
P4( mpImplPolygon
->mpFlagAry
[ i
+ 3 ] );
883 if( ( POLY_NORMAL
== P1
|| POLY_SMOOTH
== P1
|| POLY_SYMMTR
== P1
) &&
884 ( POLY_CONTROL
== mpImplPolygon
->mpFlagAry
[ i
+ 1 ] ) &&
885 ( POLY_CONTROL
== mpImplPolygon
->mpFlagAry
[ i
+ 2 ] ) &&
886 ( POLY_NORMAL
== P4
|| POLY_SMOOTH
== P4
|| POLY_SYMMTR
== P4
) )
888 ImplAdaptiveSubdivide( aPointIter
, d
*d
+1.0, 0, d
*d
,
889 mpImplPolygon
->mpPointAry
[ i
].X(), mpImplPolygon
->mpPointAry
[ i
].Y(),
890 mpImplPolygon
->mpPointAry
[ i
+1 ].X(), mpImplPolygon
->mpPointAry
[ i
+1 ].Y(),
891 mpImplPolygon
->mpPointAry
[ i
+2 ].X(), mpImplPolygon
->mpPointAry
[ i
+2 ].Y(),
892 mpImplPolygon
->mpPointAry
[ i
+3 ].X(), mpImplPolygon
->mpPointAry
[ i
+3 ].Y() );
898 *aPointIter
++ = mpImplPolygon
->mpPointAry
[ i
++ ];
900 if (aPoints
.size() >= SAL_MAX_UINT16
)
902 OSL_ENSURE(aPoints
.size() < SAL_MAX_UINT16
,
903 "Polygon::AdapativeSubdivision created polygon too many points;"
904 " using original polygon instead");
906 // The resulting polygon can not hold all the points
907 // that we have created so far. Stop the subdivision
908 // and return a copy of the unmodified polygon.
914 // fill result polygon
915 rResult
= Polygon( (sal_uInt16
)aPoints
.size() ); // ensure sufficient size for copy
916 ::std::copy(aPoints
.begin(), aPoints
.end(), rResult
.mpImplPolygon
->mpPointAry
);
920 void Polygon::ImplReduceEdges( Polygon
& rPoly
, const double& rArea
, sal_uInt16 nPercent
)
922 const double fBound
= 2000.0 * ( 100 - nPercent
) * 0.01;
923 sal_uInt16 nNumNoChange
= 0,
926 while( nNumNoChange
< 2 )
928 sal_uInt16 nPntCnt
= rPoly
.GetSize(), nNewPos
= 0;
929 Polygon
aNewPoly( nPntCnt
);
930 bool bChangeInThisRun
= false;
932 for( sal_uInt16 n
= 0; n
< nPntCnt
; n
++ )
934 bool bDeletePoint
= false;
936 if( ( n
+ nNumRuns
) % 2 )
938 sal_uInt16 nIndPrev
= !n
? nPntCnt
- 1 : n
- 1;
939 sal_uInt16 nIndPrevPrev
= !nIndPrev
? nPntCnt
- 1 : nIndPrev
- 1;
940 sal_uInt16 nIndNext
= ( n
== nPntCnt
-1 ) ? 0 : n
+ 1;
941 sal_uInt16 nIndNextNext
= ( nIndNext
== nPntCnt
- 1 ) ? 0 : nIndNext
+ 1;
942 Vector2D
aVec1( rPoly
[ nIndPrev
] ); aVec1
-= rPoly
[ nIndPrevPrev
];
943 Vector2D
aVec2( rPoly
[ n
] ); aVec2
-= rPoly
[ nIndPrev
];
944 Vector2D
aVec3( rPoly
[ nIndNext
] ); aVec3
-= rPoly
[ n
];
945 Vector2D
aVec4( rPoly
[ nIndNextNext
] ); aVec4
-= rPoly
[ nIndNext
];
946 double fDist1
= aVec1
.GetLength(), fDist2
= aVec2
.GetLength();
947 double fDist3
= aVec3
.GetLength(), fDist4
= aVec4
.GetLength();
948 double fTurnB
= aVec2
.Normalize().Scalar( aVec3
.Normalize() );
950 if( fabs( fTurnB
) < ( 1.0 + SMALL_DVALUE
) && fabs( fTurnB
) > ( 1.0 - SMALL_DVALUE
) )
954 Vector2D
aVecB( rPoly
[ nIndNext
] );
955 double fDistB
= ( aVecB
-= rPoly
[ nIndPrev
] ).GetLength();
956 double fLenWithB
= fDist2
+ fDist3
;
957 double fLenFact
= ( fDistB
!= 0.0 ) ? fLenWithB
/ fDistB
: 1.0;
958 double fTurnPrev
= aVec1
.Normalize().Scalar( aVec2
);
959 double fTurnNext
= aVec3
.Scalar( aVec4
.Normalize() );
960 double fGradPrev
, fGradB
, fGradNext
;
962 if( fabs( fTurnPrev
) < ( 1.0 + SMALL_DVALUE
) && fabs( fTurnPrev
) > ( 1.0 - SMALL_DVALUE
) )
965 fGradPrev
= acos( fTurnPrev
) / ( aVec1
.IsNegative( aVec2
) ? -F_PI180
: F_PI180
);
967 fGradB
= acos( fTurnB
) / ( aVec2
.IsNegative( aVec3
) ? -F_PI180
: F_PI180
);
969 if( fabs( fTurnNext
) < ( 1.0 + SMALL_DVALUE
) && fabs( fTurnNext
) > ( 1.0 - SMALL_DVALUE
) )
972 fGradNext
= acos( fTurnNext
) / ( aVec3
.IsNegative( aVec4
) ? -F_PI180
: F_PI180
);
974 if( ( fGradPrev
> 0.0 && fGradB
< 0.0 && fGradNext
> 0.0 ) ||
975 ( fGradPrev
< 0.0 && fGradB
> 0.0 && fGradNext
< 0.0 ) )
977 if( ( fLenFact
< ( FSQRT2
+ SMALL_DVALUE
) ) &&
978 ( ( ( fDist1
+ fDist4
) / ( fDist2
+ fDist3
) ) * 2000.0 ) > fBound
)
985 double fRelLen
= 1.0 - sqrt( fDistB
/ rArea
);
989 else if( fRelLen
> 1.0 )
992 if( ( (sal_uInt32
) ( ( ( fLenFact
- 1.0 ) * 1000000.0 ) + 0.5 ) < fBound
) &&
993 ( fabs( fGradB
) <= ( fRelLen
* fBound
* 0.01 ) ) )
1002 aNewPoly
[ nNewPos
++ ] = rPoly
[ n
];
1004 bChangeInThisRun
= true;
1007 if( bChangeInThisRun
&& nNewPos
)
1009 aNewPoly
.SetSize( nNewPos
);
1020 void Polygon::Move( long nHorzMove
, long nVertMove
)
1022 // This check is required for DrawEngine
1023 if ( !nHorzMove
&& !nVertMove
)
1029 sal_uInt16 nCount
= mpImplPolygon
->mnPoints
;
1030 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
1032 Point
* pPt
= &(mpImplPolygon
->mpPointAry
[i
]);
1033 pPt
->X() += nHorzMove
;
1034 pPt
->Y() += nVertMove
;
1038 void Polygon::Translate(const Point
& rTrans
)
1042 for ( sal_uInt16 i
= 0, nCount
= mpImplPolygon
->mnPoints
; i
< nCount
; i
++ )
1043 mpImplPolygon
->mpPointAry
[ i
] += rTrans
;
1046 void Polygon::Scale( double fScaleX
, double fScaleY
)
1050 for ( sal_uInt16 i
= 0, nCount
= mpImplPolygon
->mnPoints
; i
< nCount
; i
++ )
1052 Point
& rPnt
= mpImplPolygon
->mpPointAry
[i
];
1053 rPnt
.X() = (long) ( fScaleX
* rPnt
.X() );
1054 rPnt
.Y() = (long) ( fScaleY
* rPnt
.Y() );
1058 void Polygon::Rotate( const Point
& rCenter
, sal_uInt16 nAngle10
)
1064 const double fAngle
= F_PI1800
* nAngle10
;
1065 Rotate( rCenter
, sin( fAngle
), cos( fAngle
) );
1069 void Polygon::Rotate( const Point
& rCenter
, double fSin
, double fCos
)
1073 long nCenterX
= rCenter
.X();
1074 long nCenterY
= rCenter
.Y();
1076 for( sal_uInt16 i
= 0, nCount
= mpImplPolygon
->mnPoints
; i
< nCount
; i
++ )
1078 Point
& rPt
= mpImplPolygon
->mpPointAry
[ i
];
1080 const long nX
= rPt
.X() - nCenterX
;
1081 const long nY
= rPt
.Y() - nCenterY
;
1082 rPt
.X() = (long) FRound( fCos
* nX
+ fSin
* nY
) + nCenterX
;
1083 rPt
.Y() = -(long) FRound( fSin
* nX
- fCos
* nY
) + nCenterY
;
1087 class ImplPointFilter
1090 virtual void LastPoint() = 0;
1091 virtual void Input( const Point
& rPoint
) = 0;
1094 ~ImplPointFilter() {}
1097 class ImplPolygonPointFilter
: public ImplPointFilter
1099 std::unique_ptr
<ImplPolygon
> mxPoly
;
1102 ImplPolygonPointFilter(sal_uInt16 nDestSize
)
1103 : mxPoly(new ImplPolygon(nDestSize
))
1108 virtual ~ImplPolygonPointFilter()
1112 virtual void LastPoint() SAL_OVERRIDE
;
1113 virtual void Input( const Point
& rPoint
) SAL_OVERRIDE
;
1115 ImplPolygon
* release() { return mxPoly
.release(); }
1118 void ImplPolygonPointFilter::Input( const Point
& rPoint
)
1120 if ( !mnSize
|| (rPoint
!= mxPoly
->mpPointAry
[mnSize
-1]) )
1123 if ( mnSize
> mxPoly
->mnPoints
)
1124 mxPoly
->ImplSetSize( mnSize
);
1125 mxPoly
->mpPointAry
[mnSize
-1] = rPoint
;
1129 void ImplPolygonPointFilter::LastPoint()
1131 if ( mnSize
< mxPoly
->mnPoints
)
1132 mxPoly
->ImplSetSize( mnSize
);
1135 class ImplEdgePointFilter
: public ImplPointFilter
1139 ImplPointFilter
& mrNextFilter
;
1147 ImplEdgePointFilter( int nEdge
, long nLow
, long nHigh
,
1148 ImplPointFilter
& rNextFilter
) :
1149 mrNextFilter( rNextFilter
),
1158 virtual ~ImplEdgePointFilter() {}
1160 Point
EdgeSection( const Point
& rPoint
, int nEdge
) const;
1161 int VisibleSide( const Point
& rPoint
) const;
1162 bool IsPolygon() const
1163 { return maFirstPoint
== maLastPoint
; }
1165 virtual void Input( const Point
& rPoint
) SAL_OVERRIDE
;
1166 virtual void LastPoint() SAL_OVERRIDE
;
1169 inline int ImplEdgePointFilter::VisibleSide( const Point
& rPoint
) const
1171 if ( mnEdge
& EDGE_HORZ
)
1173 return rPoint
.X() < mnLow
? EDGE_LEFT
:
1174 rPoint
.X() > mnHigh
? EDGE_RIGHT
: 0;
1178 return rPoint
.Y() < mnLow
? EDGE_TOP
:
1179 rPoint
.Y() > mnHigh
? EDGE_BOTTOM
: 0;
1183 Point
ImplEdgePointFilter::EdgeSection( const Point
& rPoint
, int nEdge
) const
1185 long lx
= maLastPoint
.X();
1186 long ly
= maLastPoint
.Y();
1187 long md
= rPoint
.X() - lx
;
1188 long mn
= rPoint
.Y() - ly
;
1192 if ( nEdge
& EDGE_VERT
)
1194 nNewY
= (nEdge
== EDGE_TOP
) ? mnLow
: mnHigh
;
1195 long dy
= nNewY
- ly
;
1198 else if ( (LONG_MAX
/ std::abs(md
)) >= std::abs(dy
) )
1199 nNewX
= (dy
* md
) / mn
+ lx
;
1215 nNewX
= (long)ady
+ lx
;
1220 nNewX
= (nEdge
== EDGE_LEFT
) ? mnLow
: mnHigh
;
1221 long dx
= nNewX
- lx
;
1224 else if ( (LONG_MAX
/ std::abs(mn
)) >= std::abs(dx
) )
1225 nNewY
= (dx
* mn
) / md
+ ly
;
1241 nNewY
= (long)adx
+ ly
;
1245 return Point( nNewX
, nNewY
);
1248 void ImplEdgePointFilter::Input( const Point
& rPoint
)
1250 int nOutside
= VisibleSide( rPoint
);
1254 maFirstPoint
= rPoint
;
1257 mrNextFilter
.Input( rPoint
);
1259 else if ( rPoint
== maLastPoint
)
1261 else if ( !nOutside
)
1263 if ( mnLastOutside
)
1264 mrNextFilter
.Input( EdgeSection( rPoint
, mnLastOutside
) );
1265 mrNextFilter
.Input( rPoint
);
1267 else if ( !mnLastOutside
)
1268 mrNextFilter
.Input( EdgeSection( rPoint
, nOutside
) );
1269 else if ( nOutside
!= mnLastOutside
)
1271 mrNextFilter
.Input( EdgeSection( rPoint
, mnLastOutside
) );
1272 mrNextFilter
.Input( EdgeSection( rPoint
, nOutside
) );
1275 maLastPoint
= rPoint
;
1276 mnLastOutside
= nOutside
;
1279 void ImplEdgePointFilter::LastPoint()
1283 int nOutside
= VisibleSide( maFirstPoint
);
1285 if ( nOutside
!= mnLastOutside
)
1286 Input( maFirstPoint
);
1287 mrNextFilter
.LastPoint();
1291 void Polygon::Clip( const Rectangle
& rRect
, bool bPolygon
)
1293 // #105251# Justify rect before edge filtering
1294 Rectangle
aJustifiedRect( rRect
);
1295 aJustifiedRect
.Justify();
1297 sal_uInt16 nSourceSize
= mpImplPolygon
->mnPoints
;
1298 ImplPolygonPointFilter
aPolygon( nSourceSize
);
1299 ImplEdgePointFilter
aHorzFilter( EDGE_HORZ
, aJustifiedRect
.Left(), aJustifiedRect
.Right(),
1301 ImplEdgePointFilter
aVertFilter( EDGE_VERT
, aJustifiedRect
.Top(), aJustifiedRect
.Bottom(),
1304 for ( sal_uInt16 i
= 0; i
< nSourceSize
; i
++ )
1305 aVertFilter
.Input( mpImplPolygon
->mpPointAry
[i
] );
1306 if ( bPolygon
|| aVertFilter
.IsPolygon() )
1307 aVertFilter
.LastPoint();
1309 aPolygon
.LastPoint();
1311 // Delete old ImpPolygon-data and assign from ImpPolygonPointFilter
1312 if ( mpImplPolygon
->mnRefCount
)
1314 if ( mpImplPolygon
->mnRefCount
> 1 )
1315 mpImplPolygon
->mnRefCount
--;
1317 delete mpImplPolygon
;
1319 mpImplPolygon
= aPolygon
.release();
1322 Rectangle
Polygon::GetBoundRect() const
1324 // Removing the assert. Bezier curves have the attribute that each single
1325 // curve segment defined by four points can not exit the four-point polygon
1326 // defined by that points. This allows to say that the curve segment can also
1327 // never leave the Range of it's defining points.
1328 // The result is that Polygon::GetBoundRect() may not create the minimal
1329 // BoundRect of the Polygon (to get that, use basegfx::B2DPolygon classes),
1330 // but will always create a valid BoundRect, at least as long as this method
1331 // 'blindly' travels over all points, including control points.
1333 // DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetBoundRect could fail with beziers!" );
1335 sal_uInt16 nCount
= mpImplPolygon
->mnPoints
;
1339 long nXMin
, nXMax
, nYMin
, nYMax
;
1341 const Point
* pPt
= &(mpImplPolygon
->mpPointAry
[0]);
1342 nXMin
= nXMax
= pPt
->X();
1343 nYMin
= nYMax
= pPt
->Y();
1345 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
1347 pPt
= &(mpImplPolygon
->mpPointAry
[i
]);
1349 if ( pPt
->X() < nXMin
)
1351 if ( pPt
->X() > nXMax
)
1353 if ( pPt
->Y() < nYMin
)
1355 if ( pPt
->Y() > nYMax
)
1359 return Rectangle( nXMin
, nYMin
, nXMax
, nYMax
);
1362 double Polygon::GetSignedArea() const
1364 DBG_ASSERT( !mpImplPolygon
->mpFlagAry
, "GetArea could fail with beziers!" );
1368 if( mpImplPolygon
->mnPoints
> 2 )
1370 const sal_uInt16 nCount1
= mpImplPolygon
->mnPoints
- 1;
1372 for( sal_uInt16 i
= 0; i
< nCount1
; )
1374 const Point
& rPt
= mpImplPolygon
->mpPointAry
[ i
];
1375 const Point
& rPt1
= mpImplPolygon
->mpPointAry
[ ++i
];
1376 fArea
+= ( rPt
.X() - rPt1
.X() ) * ( rPt
.Y() + rPt1
.Y() );
1379 const Point
& rPt
= mpImplPolygon
->mpPointAry
[ nCount1
];
1380 const Point
& rPt0
= mpImplPolygon
->mpPointAry
[ 0 ];
1381 fArea
+= ( rPt
.X() - rPt0
.X() ) * ( rPt
.Y() + rPt0
.Y() );
1387 bool Polygon::IsInside( const Point
& rPoint
) const
1389 DBG_ASSERT( !mpImplPolygon
->mpFlagAry
, "IsInside could fail with beziers!" );
1391 const Rectangle
aBound( GetBoundRect() );
1392 const Line
aLine( rPoint
, Point( aBound
.Right() + 100L, rPoint
.Y() ) );
1393 sal_uInt16 nCount
= mpImplPolygon
->mnPoints
;
1394 sal_uInt16 nPCounter
= 0;
1396 if ( ( nCount
> 2 ) && aBound
.IsInside( rPoint
) )
1398 Point
aPt1( mpImplPolygon
->mpPointAry
[ 0 ] );
1399 Point aIntersection
;
1400 Point aLastIntersection
;
1402 while ( ( aPt1
== mpImplPolygon
->mpPointAry
[ nCount
- 1 ] ) && ( nCount
> 3 ) )
1405 for ( sal_uInt16 i
= 1; i
<= nCount
; i
++ )
1407 const Point
& rPt2
= mpImplPolygon
->mpPointAry
[ ( i
< nCount
) ? i
: 0 ];
1409 if ( aLine
.Intersection( Line( aPt1
, rPt2
), aIntersection
) )
1411 // This avoids insertion of double intersections
1414 if ( aIntersection
!= aLastIntersection
)
1416 aLastIntersection
= aIntersection
;
1422 aLastIntersection
= aIntersection
;
1431 // is inside, if number of intersection points is odd
1432 return ( ( nPCounter
& 1 ) == 1 );
1435 bool Polygon::IsRightOrientated() const
1437 return GetSignedArea() >= 0.0;
1440 void Polygon::Insert( sal_uInt16 nPos
, const Point
& rPt
, PolyFlags eFlags
)
1444 if( nPos
>= mpImplPolygon
->mnPoints
)
1445 nPos
= mpImplPolygon
->mnPoints
;
1447 mpImplPolygon
->ImplSplit( nPos
, 1 );
1448 mpImplPolygon
->mpPointAry
[ nPos
] = rPt
;
1450 if( POLY_NORMAL
!= eFlags
)
1452 mpImplPolygon
->ImplCreateFlagArray();
1453 mpImplPolygon
->mpFlagAry
[ nPos
] = (sal_uInt8
) eFlags
;
1457 void Polygon::Insert( sal_uInt16 nPos
, const Polygon
& rPoly
)
1459 const sal_uInt16 nInsertCount
= rPoly
.mpImplPolygon
->mnPoints
;
1465 if( nPos
>= mpImplPolygon
->mnPoints
)
1466 nPos
= mpImplPolygon
->mnPoints
;
1468 if( rPoly
.mpImplPolygon
->mpFlagAry
)
1469 mpImplPolygon
->ImplCreateFlagArray();
1471 mpImplPolygon
->ImplSplit( nPos
, nInsertCount
, rPoly
.mpImplPolygon
);
1475 Point
& Polygon::operator[]( sal_uInt16 nPos
)
1477 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
, "Polygon::[]: nPos >= nPoints" );
1480 return mpImplPolygon
->mpPointAry
[nPos
];
1483 Polygon
& Polygon::operator=( const Polygon
& rPoly
)
1485 DBG_ASSERT( rPoly
.mpImplPolygon
->mnRefCount
< 0xFFFFFFFE, "Polygon: RefCount overflow" );
1487 // Increase refcounter before assigning
1488 // Note: RefCount == 0 for static objects
1489 if ( rPoly
.mpImplPolygon
->mnRefCount
)
1490 rPoly
.mpImplPolygon
->mnRefCount
++;
1492 // Delete if recount == 0, otherwise decrement
1493 if ( mpImplPolygon
->mnRefCount
)
1495 if ( mpImplPolygon
->mnRefCount
> 1 )
1496 mpImplPolygon
->mnRefCount
--;
1498 delete mpImplPolygon
;
1501 mpImplPolygon
= rPoly
.mpImplPolygon
;
1505 bool Polygon::operator==( const Polygon
& rPoly
) const
1508 if ( (rPoly
.mpImplPolygon
== mpImplPolygon
) )
1514 bool Polygon::IsEqual( const Polygon
& rPoly
) const
1516 bool bIsEqual
= true;
1518 if ( GetSize() != rPoly
.GetSize() )
1522 for ( i
= 0; i
< GetSize(); i
++ )
1524 if ( ( GetPoint( i
) != rPoly
.GetPoint( i
) ) ||
1525 ( GetFlags( i
) != rPoly
.GetFlags( i
) ) )
1535 SvStream
& ReadPolygon( SvStream
& rIStream
, Polygon
& rPoly
)
1537 DBG_ASSERTWARNING( rIStream
.GetVersion(), "Polygon::>> - Solar-Version not set on rIStream" );
1540 sal_uInt16
nPoints(0);
1542 // read all points and create array
1543 rIStream
.ReadUInt16( nPoints
);
1545 const size_t nMaxRecordsPossible
= rIStream
.remainingSize() / (2 * sizeof(sal_Int32
));
1546 if (nPoints
> nMaxRecordsPossible
)
1548 SAL_WARN("tools", "Polygon claims " << nPoints
<< " records, but only " << nMaxRecordsPossible
<< " possible");
1549 nPoints
= nMaxRecordsPossible
;
1552 if ( rPoly
.mpImplPolygon
->mnRefCount
!= 1 )
1554 if ( rPoly
.mpImplPolygon
->mnRefCount
)
1555 rPoly
.mpImplPolygon
->mnRefCount
--;
1556 rPoly
.mpImplPolygon
= new ImplPolygon( nPoints
);
1559 rPoly
.mpImplPolygon
->ImplSetSize( nPoints
, false );
1562 // Determine whether we need to write through operators
1563 #if (SAL_TYPES_SIZEOFLONG) == 4
1564 #ifdef OSL_BIGENDIAN
1565 if ( rIStream
.GetEndian() == SvStreamEndian::BIG
)
1567 if ( rIStream
.GetEndian() == SvStreamEndian::LITTLE
)
1569 rIStream
.Read( rPoly
.mpImplPolygon
->mpPointAry
, nPoints
*sizeof(Point
) );
1573 for( i
= 0; i
< nPoints
; i
++ )
1575 sal_Int32
nTmpX(0), nTmpY(0);
1576 rIStream
.ReadInt32( nTmpX
).ReadInt32( nTmpY
);
1577 rPoly
.mpImplPolygon
->mpPointAry
[i
].X() = nTmpX
;
1578 rPoly
.mpImplPolygon
->mpPointAry
[i
].Y() = nTmpY
;
1586 SvStream
& WritePolygon( SvStream
& rOStream
, const Polygon
& rPoly
)
1588 DBG_ASSERTWARNING( rOStream
.GetVersion(), "Polygon::<< - Solar-Version not set on rOStream" );
1591 sal_uInt16 nPoints
= rPoly
.GetSize();
1593 // Write number of points
1594 rOStream
.WriteUInt16( nPoints
);
1597 // Determine whether we need to write through operators
1598 #if (SAL_TYPES_SIZEOFLONG) == 4
1599 #ifdef OSL_BIGENDIAN
1600 if ( rOStream
.GetEndian() == SvStreamEndian::BIG
)
1602 if ( rOStream
.GetEndian() == SvStreamEndian::LITTLE
)
1606 rOStream
.Write( rPoly
.mpImplPolygon
->mpPointAry
, nPoints
*sizeof(Point
) );
1611 for( i
= 0; i
< nPoints
; i
++ )
1613 rOStream
.WriteInt32( rPoly
.mpImplPolygon
->mpPointAry
[i
].X() )
1614 .WriteInt32( rPoly
.mpImplPolygon
->mpPointAry
[i
].Y() );
1622 void Polygon::ImplRead( SvStream
& rIStream
)
1624 sal_uInt8
bHasPolyFlags(0);
1626 ReadPolygon( rIStream
, *this );
1627 rIStream
.ReadUChar( bHasPolyFlags
);
1629 if ( bHasPolyFlags
)
1631 mpImplPolygon
->mpFlagAry
= new sal_uInt8
[ mpImplPolygon
->mnPoints
];
1632 rIStream
.Read( mpImplPolygon
->mpFlagAry
, mpImplPolygon
->mnPoints
);
1636 void Polygon::Read( SvStream
& rIStream
)
1638 VersionCompat
aCompat( rIStream
, StreamMode::READ
);
1640 ImplRead( rIStream
);
1643 void Polygon::ImplWrite( SvStream
& rOStream
) const
1645 bool bHasPolyFlags
= mpImplPolygon
->mpFlagAry
!= NULL
;
1646 WritePolygon( rOStream
, *this );
1647 rOStream
.WriteBool(bHasPolyFlags
);
1649 if ( bHasPolyFlags
)
1650 rOStream
.Write( mpImplPolygon
->mpFlagAry
, mpImplPolygon
->mnPoints
);
1653 void Polygon::Write( SvStream
& rOStream
) const
1655 VersionCompat
aCompat( rOStream
, StreamMode::WRITE
, 1 );
1657 ImplWrite( rOStream
);
1660 // #i74631#/#i115917# numerical correction method for B2DPolygon
1661 void impCorrectContinuity(basegfx::B2DPolygon
& roPolygon
, sal_uInt32 nIndex
, sal_uInt8 nCFlag
)
1663 const sal_uInt32
nPointCount(roPolygon
.count());
1664 OSL_ENSURE(nIndex
< nPointCount
, "impCorrectContinuity: index access out of range (!)");
1666 if(nIndex
< nPointCount
&& (POLY_SMOOTH
== nCFlag
|| POLY_SYMMTR
== nCFlag
))
1668 if(roPolygon
.isPrevControlPointUsed(nIndex
) && roPolygon
.isNextControlPointUsed(nIndex
))
1670 // #i115917# Patch from osnola (modified, thanks for showing the porblem)
1672 // The correction is needed because an integer polygon with control points
1673 // is converted to double precision. When C1 or C2 is used the involved vectors
1674 // may not have the same directions/lengths since these come from integer coordinates
1675 // and may have been snapped to different nearest integer coordinates. The snap error
1676 // is in the range of +-1 in y and y, thus 0.0 <= error <= sqrt(2.0). Nonetheless,
1677 // it needs to be corrected to be able to detect the continuity in this points
1680 // We only have the integer data here (already in double precision form, but no mantisses
1681 // used), so the best correction is to use:
1683 // for C1: The longest vector since it potentially has best preserved the original vector.
1684 // Even better the sum of the vectors, weighted by their length. This gives the
1685 // normal vector addition to get the vector itself, lengths need to be preserved.
1686 // for C2: The mediated vector(s) since both should be the same, but mirrored
1688 // extract the point and vectors
1689 const basegfx::B2DPoint
aPoint(roPolygon
.getB2DPoint(nIndex
));
1690 const basegfx::B2DVector
aNext(roPolygon
.getNextControlPoint(nIndex
) - aPoint
);
1691 const basegfx::B2DVector
aPrev(aPoint
- roPolygon
.getPrevControlPoint(nIndex
));
1693 // calculate common direction vector, normalize
1694 const basegfx::B2DVector
aDirection(aNext
+ aPrev
);
1696 if(POLY_SMOOTH
== nCFlag
)
1698 // C1: apply common direction vector, preserve individual lengths
1699 const double fInvDirectionLen(1.0 / aDirection
.getLength());
1700 roPolygon
.setNextControlPoint(nIndex
, basegfx::B2DPoint(aPoint
+ (aDirection
* (aNext
.getLength() * fInvDirectionLen
))));
1701 roPolygon
.setPrevControlPoint(nIndex
, basegfx::B2DPoint(aPoint
- (aDirection
* (aPrev
.getLength() * fInvDirectionLen
))));
1705 // C2: get mediated length. Taking half of the unnormalized direction would be
1706 // an approximation, but not correct.
1707 const double fMedLength((aNext
.getLength() + aPrev
.getLength()) * (0.5 / aDirection
.getLength()));
1708 const basegfx::B2DVector
aScaledDirection(aDirection
* fMedLength
);
1710 // Bring Direction to correct length and apply
1711 roPolygon
.setNextControlPoint(nIndex
, basegfx::B2DPoint(aPoint
+ aScaledDirection
));
1712 roPolygon
.setPrevControlPoint(nIndex
, basegfx::B2DPoint(aPoint
- aScaledDirection
));
1718 // convert to basegfx::B2DPolygon and return
1719 basegfx::B2DPolygon
Polygon::getB2DPolygon() const
1721 basegfx::B2DPolygon aRetval
;
1722 const sal_uInt16
nCount(mpImplPolygon
->mnPoints
);
1726 if(mpImplPolygon
->mpFlagAry
)
1728 // handling for curves. Add start point
1729 const Point
aStartPoint(mpImplPolygon
->mpPointAry
[0]);
1730 sal_uInt8
nPointFlag(mpImplPolygon
->mpFlagAry
[0]);
1731 aRetval
.append(basegfx::B2DPoint(aStartPoint
.X(), aStartPoint
.Y()));
1732 Point aControlA
, aControlB
;
1734 for(sal_uInt16
a(1); a
< nCount
;)
1736 bool bControlA(false);
1737 bool bControlB(false);
1739 if(POLY_CONTROL
== mpImplPolygon
->mpFlagAry
[a
])
1741 aControlA
= mpImplPolygon
->mpPointAry
[a
++];
1745 if(a
< nCount
&& POLY_CONTROL
== mpImplPolygon
->mpFlagAry
[a
])
1747 aControlB
= mpImplPolygon
->mpPointAry
[a
++];
1751 // assert invalid polygons
1752 OSL_ENSURE(bControlA
== bControlB
, "Polygon::getB2DPolygon: Invalid source polygon (!)");
1757 const Point
aEndPoint(mpImplPolygon
->mpPointAry
[a
]);
1762 aRetval
.appendBezierSegment(
1763 basegfx::B2DPoint(aControlA
.X(), aControlA
.Y()),
1764 basegfx::B2DPoint(aControlB
.X(), aControlB
.Y()),
1765 basegfx::B2DPoint(aEndPoint
.X(), aEndPoint
.Y()));
1767 impCorrectContinuity(aRetval
, aRetval
.count() - 2, nPointFlag
);
1771 // no bezier edge, add end point
1772 aRetval
.append(basegfx::B2DPoint(aEndPoint
.X(), aEndPoint
.Y()));
1775 nPointFlag
= mpImplPolygon
->mpFlagAry
[a
++];
1779 // if exist, remove double first/last points, set closed and correct control points
1780 basegfx::tools::checkClosed(aRetval
);
1782 if(aRetval
.isClosed())
1784 // closeWithGeometryChange did really close, so last point(s) were removed.
1785 // Correct the continuity in the changed point
1786 impCorrectContinuity(aRetval
, 0, mpImplPolygon
->mpFlagAry
[0]);
1791 // extra handling for non-curves (most-used case) for speedup
1792 for(sal_uInt16
a(0); a
< nCount
; a
++)
1794 // get point and add
1795 const Point
aPoint(mpImplPolygon
->mpPointAry
[a
]);
1796 aRetval
.append(basegfx::B2DPoint(aPoint
.X(), aPoint
.Y()));
1800 basegfx::tools::checkClosed(aRetval
);
1807 // constructor to convert from basegfx::B2DPolygon
1808 // #i76891# Needed to change from adding all control points (even for unused
1809 // edges) and creating a fixed-size Polygon in the first run to creating the
1810 // minimal Polygon. This requires a temporary Point- and Flag-Array for curves
1811 // and a memcopy at ImplPolygon creation, but contains no zero-controlpoints
1812 // for straight edges.
1813 Polygon::Polygon(const basegfx::B2DPolygon
& rPolygon
)
1816 const bool bCurve(rPolygon
.areControlPointsUsed());
1817 const bool bClosed(rPolygon
.isClosed());
1818 sal_uInt32
nB2DLocalCount(rPolygon
.count());
1822 // #127979# Reduce source point count hard to the limit of the tools Polygon
1823 if(nB2DLocalCount
> ((0x0000ffff / 3L) - 1L))
1825 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)");
1826 nB2DLocalCount
= ((0x0000ffff / 3L) - 1L);
1829 // calculate target point count
1830 const sal_uInt32
nLoopCount(bClosed
? nB2DLocalCount
: (nB2DLocalCount
? nB2DLocalCount
- 1L : 0L ));
1834 // calculate maximum array size and allocate; prepare insert index
1835 const sal_uInt32
nMaxTargetCount((nLoopCount
* 3) + 1);
1836 mpImplPolygon
= new ImplPolygon(static_cast< sal_uInt16
>(nMaxTargetCount
), true);
1838 // prepare insert index and current point
1839 sal_uInt32
nArrayInsert(0);
1840 basegfx::B2DCubicBezier aBezier
;
1841 aBezier
.setStartPoint(rPolygon
.getB2DPoint(0));
1843 for(sal_uInt32
a(0L); a
< nLoopCount
; a
++)
1845 // add current point (always) and remember StartPointIndex for evtl. later corrections
1846 const Point
aStartPoint(FRound(aBezier
.getStartPoint().getX()), FRound(aBezier
.getStartPoint().getY()));
1847 const sal_uInt32
nStartPointIndex(nArrayInsert
);
1848 mpImplPolygon
->mpPointAry
[nStartPointIndex
] = aStartPoint
;
1849 mpImplPolygon
->mpFlagAry
[nStartPointIndex
] = (sal_uInt8
)POLY_NORMAL
;
1852 // prepare next segment
1853 const sal_uInt32
nNextIndex((a
+ 1) % nB2DLocalCount
);
1854 aBezier
.setEndPoint(rPolygon
.getB2DPoint(nNextIndex
));
1855 aBezier
.setControlPointA(rPolygon
.getNextControlPoint(a
));
1856 aBezier
.setControlPointB(rPolygon
.getPrevControlPoint(nNextIndex
));
1858 if(aBezier
.isBezier())
1860 // if one is used, add always two control points due to the old schema
1861 mpImplPolygon
->mpPointAry
[nArrayInsert
] = Point(FRound(aBezier
.getControlPointA().getX()), FRound(aBezier
.getControlPointA().getY()));
1862 mpImplPolygon
->mpFlagAry
[nArrayInsert
] = (sal_uInt8
)POLY_CONTROL
;
1865 mpImplPolygon
->mpPointAry
[nArrayInsert
] = Point(FRound(aBezier
.getControlPointB().getX()), FRound(aBezier
.getControlPointB().getY()));
1866 mpImplPolygon
->mpFlagAry
[nArrayInsert
] = (sal_uInt8
)POLY_CONTROL
;
1870 // test continuity with previous control point to set flag value
1871 if(aBezier
.getControlPointA() != aBezier
.getStartPoint() && (bClosed
|| a
))
1873 const basegfx::B2VectorContinuity
eCont(rPolygon
.getContinuityInPoint(a
));
1875 if(basegfx::CONTINUITY_C1
== eCont
)
1877 mpImplPolygon
->mpFlagAry
[nStartPointIndex
] = (sal_uInt8
)POLY_SMOOTH
;
1879 else if(basegfx::CONTINUITY_C2
== eCont
)
1881 mpImplPolygon
->mpFlagAry
[nStartPointIndex
] = (sal_uInt8
)POLY_SYMMTR
;
1885 // prepare next polygon step
1886 aBezier
.setStartPoint(aBezier
.getEndPoint());
1891 // add first point again as closing point due to old definition
1892 mpImplPolygon
->mpPointAry
[nArrayInsert
] = mpImplPolygon
->mpPointAry
[0];
1893 mpImplPolygon
->mpFlagAry
[nArrayInsert
] = (sal_uInt8
)POLY_NORMAL
;
1898 // add last point as closing point
1899 const basegfx::B2DPoint
aClosingPoint(rPolygon
.getB2DPoint(nB2DLocalCount
- 1L));
1900 const Point
aEnd(FRound(aClosingPoint
.getX()), FRound(aClosingPoint
.getY()));
1901 mpImplPolygon
->mpPointAry
[nArrayInsert
] = aEnd
;
1902 mpImplPolygon
->mpFlagAry
[nArrayInsert
] = (sal_uInt8
)POLY_NORMAL
;
1906 DBG_ASSERT(nArrayInsert
<= nMaxTargetCount
, "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)");
1908 if(nArrayInsert
!= nMaxTargetCount
)
1910 mpImplPolygon
->ImplSetSize(static_cast< sal_uInt16
>(nArrayInsert
), true);
1916 // #127979# Reduce source point count hard to the limit of the tools Polygon
1917 if(nB2DLocalCount
> (0x0000ffff - 1L))
1919 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)");
1920 nB2DLocalCount
= (0x0000ffff - 1L);
1925 // point list creation
1926 const sal_uInt32
nTargetCount(nB2DLocalCount
+ (bClosed
? 1L : 0L));
1927 mpImplPolygon
= new ImplPolygon( static_cast< sal_uInt16
>(nTargetCount
) );
1928 sal_uInt16
nIndex(0);
1930 for(sal_uInt32
a(0L); a
< nB2DLocalCount
; a
++)
1932 basegfx::B2DPoint
aB2DPoint(rPolygon
.getB2DPoint(a
));
1933 Point
aPoint(FRound(aB2DPoint
.getX()), FRound(aB2DPoint
.getY()));
1934 mpImplPolygon
->mpPointAry
[nIndex
++] = aPoint
;
1939 // add first point as closing point
1940 mpImplPolygon
->mpPointAry
[nIndex
] = mpImplPolygon
->mpPointAry
[0];
1947 // no content yet, create empty polygon
1948 mpImplPolygon
= static_cast<ImplPolygon
*>(&aStaticImplPolygon
);
1952 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */