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/bigint.hxx>
22 #include <tools/debug.hxx>
23 #include <tools/helpers.hxx>
24 #include <tools/stream.hxx>
25 #include <tools/vcompat.hxx>
26 #include <tools/gen.hxx>
28 #include <tools/line.hxx>
29 #include <tools/vector2d.hxx>
30 #include <tools/poly.hxx>
31 #include <basegfx/polygon/b2dpolygon.hxx>
32 #include <basegfx/point/b2dpoint.hxx>
33 #include <basegfx/vector/b2dvector.hxx>
34 #include <basegfx/polygon/b2dpolygontools.hxx>
35 #include <basegfx/curve/b2dcubicbezier.hxx>
50 #define EDGE_HORZ (EDGE_RIGHT | EDGE_LEFT)
51 #define EDGE_VERT (EDGE_TOP | EDGE_BOTTOM)
52 #define SMALL_DVALUE 0.0000001
53 #define FSQRT2 1.4142135623730950488016887242097
55 static ImplPolygonData aStaticImplPolygon
=
60 ImplPolygon::ImplPolygon( sal_uInt16 nInitSize
, sal_Bool bFlags
)
64 mpPointAry
= (Point
*)new char[(sal_uIntPtr
)nInitSize
*sizeof(Point
)];
65 memset( mpPointAry
, 0, (sal_uIntPtr
)nInitSize
*sizeof(Point
) );
72 mpFlagAry
= new sal_uInt8
[ nInitSize
];
73 memset( mpFlagAry
, 0, nInitSize
);
82 ImplPolygon::ImplPolygon( const ImplPolygon
& rImpPoly
)
84 if ( rImpPoly
.mnPoints
)
86 mpPointAry
= (Point
*)new char[(sal_uIntPtr
)rImpPoly
.mnPoints
*sizeof(Point
)];
87 memcpy( mpPointAry
, rImpPoly
.mpPointAry
, (sal_uIntPtr
)rImpPoly
.mnPoints
*sizeof(Point
) );
89 if( rImpPoly
.mpFlagAry
)
91 mpFlagAry
= new sal_uInt8
[ rImpPoly
.mnPoints
];
92 memcpy( mpFlagAry
, rImpPoly
.mpFlagAry
, rImpPoly
.mnPoints
);
104 mnPoints
= rImpPoly
.mnPoints
;
107 ImplPolygon::ImplPolygon( sal_uInt16 nInitSize
, const Point
* pInitAry
, const sal_uInt8
* pInitFlags
)
111 mpPointAry
= (Point
*)new char[(sal_uIntPtr
)nInitSize
*sizeof(Point
)];
112 memcpy( mpPointAry
, pInitAry
, (sal_uIntPtr
)nInitSize
*sizeof( Point
) );
116 mpFlagAry
= new sal_uInt8
[ nInitSize
];
117 memcpy( mpFlagAry
, pInitFlags
, nInitSize
);
129 mnPoints
= nInitSize
;
132 ImplPolygon::~ImplPolygon()
136 delete[] (char*) mpPointAry
;
143 void ImplPolygon::ImplSetSize( sal_uInt16 nNewSize
, sal_Bool bResize
)
145 if( mnPoints
== nNewSize
)
152 pNewAry
= (Point
*)new char[(sal_uIntPtr
)nNewSize
*sizeof(Point
)];
156 // Alte Punkte kopieren
157 if ( mnPoints
< nNewSize
)
159 // Neue Punkte mit 0 initialisieren
160 memset( pNewAry
+mnPoints
, 0, (sal_uIntPtr
)(nNewSize
-mnPoints
)*sizeof(Point
) );
162 memcpy( pNewAry
, mpPointAry
, mnPoints
*sizeof(Point
) );
167 memcpy( pNewAry
, mpPointAry
, (sal_uIntPtr
)nNewSize
*sizeof(Point
) );
175 delete[] (char*) mpPointAry
;
177 // ggf. FlagArray beruecksichtigen
180 sal_uInt8
* pNewFlagAry
;
184 pNewFlagAry
= new sal_uInt8
[ nNewSize
];
188 // Alte Flags kopieren
189 if ( mnPoints
< nNewSize
)
191 // Neue Punkte mit 0 initialisieren
192 memset( pNewFlagAry
+mnPoints
, 0, nNewSize
-mnPoints
);
193 memcpy( pNewFlagAry
, mpFlagAry
, mnPoints
);
196 memcpy( pNewFlagAry
, mpFlagAry
, nNewSize
);
203 mpFlagAry
= pNewFlagAry
;
206 mpPointAry
= pNewAry
;
210 void ImplPolygon::ImplSplit( sal_uInt16 nPos
, sal_uInt16 nSpace
, ImplPolygon
* pInitPoly
)
212 const sal_uIntPtr nSpaceSize
= nSpace
* sizeof( Point
);
214 //Can't fit this in :-(, throw ?
215 if (mnPoints
+ nSpace
> USHRT_MAX
)
218 const sal_uInt16 nNewSize
= mnPoints
+ nSpace
;
220 if( nPos
>= mnPoints
)
222 // Append at the back
224 ImplSetSize( nNewSize
, sal_True
);
228 memcpy( mpPointAry
+ nPos
, pInitPoly
->mpPointAry
, nSpaceSize
);
230 if( pInitPoly
->mpFlagAry
)
231 memcpy( mpFlagAry
+ nPos
, pInitPoly
->mpFlagAry
, nSpace
);
236 const sal_uInt16 nSecPos
= nPos
+ nSpace
;
237 const sal_uInt16 nRest
= mnPoints
- nPos
;
239 Point
* pNewAry
= (Point
*) new char[ (sal_uIntPtr
) nNewSize
* sizeof( Point
) ];
241 memcpy( pNewAry
, mpPointAry
, nPos
* sizeof( Point
) );
244 memcpy( pNewAry
+ nPos
, pInitPoly
->mpPointAry
, nSpaceSize
);
246 memset( pNewAry
+ nPos
, 0, nSpaceSize
);
248 memcpy( pNewAry
+ nSecPos
, mpPointAry
+ nPos
, nRest
* sizeof( Point
) );
249 delete[] (char*) mpPointAry
;
251 // consider FlagArray
254 sal_uInt8
* pNewFlagAry
= new sal_uInt8
[ nNewSize
];
256 memcpy( pNewFlagAry
, mpFlagAry
, nPos
);
258 if( pInitPoly
&& pInitPoly
->mpFlagAry
)
259 memcpy( pNewFlagAry
+ nPos
, pInitPoly
->mpFlagAry
, nSpace
);
261 memset( pNewFlagAry
+ nPos
, 0, nSpace
);
263 memcpy( pNewFlagAry
+ nSecPos
, mpFlagAry
+ nPos
, nRest
);
265 mpFlagAry
= pNewFlagAry
;
268 mpPointAry
= pNewAry
;
273 void ImplPolygon::ImplCreateFlagArray()
277 mpFlagAry
= new sal_uInt8
[ mnPoints
];
278 memset( mpFlagAry
, 0, mnPoints
);
282 inline void Polygon::ImplMakeUnique()
284 // copy references if any exist
285 if ( mpImplPolygon
->mnRefCount
!= 1 )
287 if ( mpImplPolygon
->mnRefCount
)
288 mpImplPolygon
->mnRefCount
--;
289 mpImplPolygon
= new ImplPolygon( *mpImplPolygon
);
293 inline double ImplGetParameter( const Point
& rCenter
, const Point
& rPt
, double fWR
, double fHR
)
295 const long nDX
= rPt
.X() - rCenter
.X();
296 double fAngle
= atan2( -rPt
.Y() + rCenter
.Y(), ( ( nDX
== 0L ) ? 0.000000001 : nDX
) );
298 return atan2(fWR
*sin(fAngle
), fHR
*cos(fAngle
));
303 DBG_CTOR( Polygon
, NULL
);
304 mpImplPolygon
= (ImplPolygon
*)(&aStaticImplPolygon
);
307 Polygon::Polygon( sal_uInt16 nSize
)
309 DBG_CTOR( Polygon
, NULL
);
312 mpImplPolygon
= new ImplPolygon( nSize
);
314 mpImplPolygon
= (ImplPolygon
*)(&aStaticImplPolygon
);
317 Polygon::Polygon( sal_uInt16 nPoints
, const Point
* pPtAry
, const sal_uInt8
* pFlagAry
)
319 DBG_CTOR( Polygon
, NULL
);
322 mpImplPolygon
= new ImplPolygon( nPoints
, pPtAry
, pFlagAry
);
324 mpImplPolygon
= (ImplPolygon
*)(&aStaticImplPolygon
);
327 Polygon::Polygon( const Polygon
& rPoly
)
329 DBG_CTOR( Polygon
, NULL
);
330 DBG_CHKOBJ( &rPoly
, Polygon
, NULL
);
331 DBG_ASSERT( rPoly
.mpImplPolygon
->mnRefCount
< 0xFFFFFFFE, "Polygon: RefCount overflow" );
333 mpImplPolygon
= rPoly
.mpImplPolygon
;
334 if ( mpImplPolygon
->mnRefCount
)
335 mpImplPolygon
->mnRefCount
++;
338 Polygon::Polygon( const Rectangle
& rRect
)
340 DBG_CTOR( Polygon
, NULL
);
342 if ( rRect
.IsEmpty() )
343 mpImplPolygon
= (ImplPolygon
*)(&aStaticImplPolygon
);
346 mpImplPolygon
= new ImplPolygon( 5 );
347 mpImplPolygon
->mpPointAry
[0] = rRect
.TopLeft();
348 mpImplPolygon
->mpPointAry
[1] = rRect
.TopRight();
349 mpImplPolygon
->mpPointAry
[2] = rRect
.BottomRight();
350 mpImplPolygon
->mpPointAry
[3] = rRect
.BottomLeft();
351 mpImplPolygon
->mpPointAry
[4] = rRect
.TopLeft();
355 Polygon::Polygon( const Rectangle
& rRect
, sal_uIntPtr nHorzRound
, sal_uIntPtr nVertRound
)
357 DBG_CTOR( Polygon
, NULL
);
359 if ( rRect
.IsEmpty() )
360 mpImplPolygon
= (ImplPolygon
*)(&aStaticImplPolygon
);
363 Rectangle
aRect( rRect
);
364 aRect
.Justify(); // SJ: i9140
366 nHorzRound
= std::min( nHorzRound
, (sal_uIntPtr
) labs( aRect
.GetWidth() >> 1 ) );
367 nVertRound
= std::min( nVertRound
, (sal_uIntPtr
) labs( aRect
.GetHeight() >> 1 ) );
369 if( !nHorzRound
&& !nVertRound
)
371 mpImplPolygon
= new ImplPolygon( 5 );
372 mpImplPolygon
->mpPointAry
[0] = aRect
.TopLeft();
373 mpImplPolygon
->mpPointAry
[1] = aRect
.TopRight();
374 mpImplPolygon
->mpPointAry
[2] = aRect
.BottomRight();
375 mpImplPolygon
->mpPointAry
[3] = aRect
.BottomLeft();
376 mpImplPolygon
->mpPointAry
[4] = aRect
.TopLeft();
380 const Point
aTL( aRect
.Left() + nHorzRound
, aRect
.Top() + nVertRound
);
381 const Point
aTR( aRect
.Right() - nHorzRound
, aRect
.Top() + nVertRound
);
382 const Point
aBR( aRect
.Right() - nHorzRound
, aRect
.Bottom() - nVertRound
);
383 const Point
aBL( aRect
.Left() + nHorzRound
, aRect
.Bottom() - nVertRound
);
384 Polygon
* pEllipsePoly
= new Polygon( Point(), nHorzRound
, nVertRound
);
385 sal_uInt16 i
, nEnd
, nSize4
= pEllipsePoly
->GetSize() >> 2;
387 mpImplPolygon
= new ImplPolygon( pEllipsePoly
->GetSize() + 1 );
389 const Point
* pSrcAry
= pEllipsePoly
->GetConstPointAry();
390 Point
* pDstAry
= mpImplPolygon
->mpPointAry
;
392 for( i
= 0, nEnd
= nSize4
; i
< nEnd
; i
++ )
393 ( pDstAry
[ i
] = pSrcAry
[ i
] ) += aTR
;
395 for( nEnd
= nEnd
+ nSize4
; i
< nEnd
; i
++ )
396 ( pDstAry
[ i
] = pSrcAry
[ i
] ) += aTL
;
398 for( nEnd
= nEnd
+ nSize4
; i
< nEnd
; i
++ )
399 ( pDstAry
[ i
] = pSrcAry
[ i
] ) += aBL
;
401 for( nEnd
= nEnd
+ nSize4
; i
< nEnd
; i
++ )
402 ( pDstAry
[ i
] = pSrcAry
[ i
] ) += aBR
;
404 pDstAry
[ nEnd
] = pDstAry
[ 0 ];
410 Polygon::Polygon( const Point
& rCenter
, long nRadX
, long nRadY
, sal_uInt16 nPoints
)
412 DBG_CTOR( Polygon
, NULL
);
416 // Compute default (depends on size)
419 nPoints
= (sal_uInt16
) ( F_PI
* ( 1.5 * ( nRadX
+ nRadY
) -
420 sqrt( (double) labs( nRadX
* nRadY
) ) ) );
422 nPoints
= (sal_uInt16
) MinMax( nPoints
, 32, 256 );
424 if( ( nRadX
> 32 ) && ( nRadY
> 32 ) && ( nRadX
+ nRadY
) < 8192 )
428 // Ceil number of points until divisible by four
429 mpImplPolygon
= new ImplPolygon( nPoints
= (nPoints
+ 3) & ~3 );
433 sal_uInt16 nPoints2
= nPoints
>> 1;
434 sal_uInt16 nPoints4
= nPoints
>> 2;
436 double nAngleStep
= F_PI2
/ ( nPoints4
- 1 );
438 for( i
=0, nAngle
= 0.0; i
< nPoints4
; i
++, nAngle
+= nAngleStep
)
440 long nX
= FRound( nRadX
* cos( nAngle
) );
441 long nY
= FRound( -nRadY
* sin( nAngle
) );
443 pPt
= &(mpImplPolygon
->mpPointAry
[i
]);
444 pPt
->X() = nX
+ rCenter
.X();
445 pPt
->Y() = nY
+ rCenter
.Y();
446 pPt
= &(mpImplPolygon
->mpPointAry
[nPoints2
-i
-1]);
447 pPt
->X() = -nX
+ rCenter
.X();
448 pPt
->Y() = nY
+ rCenter
.Y();
449 pPt
= &(mpImplPolygon
->mpPointAry
[i
+nPoints2
]);
450 pPt
->X() = -nX
+ rCenter
.X();
451 pPt
->Y() = -nY
+ rCenter
.Y();
452 pPt
= &(mpImplPolygon
->mpPointAry
[nPoints
-i
-1]);
453 pPt
->X() = nX
+ rCenter
.X();
454 pPt
->Y() = -nY
+ rCenter
.Y();
458 mpImplPolygon
= (ImplPolygon
*)(&aStaticImplPolygon
);
461 Polygon::Polygon( const Rectangle
& rBound
, const Point
& rStart
, const Point
& rEnd
,
462 PolyStyle eStyle
, sal_Bool bFullCircle
)
464 DBG_CTOR( Polygon
, NULL
);
466 const long nWidth
= rBound
.GetWidth();
467 const long nHeight
= rBound
.GetHeight();
469 if( ( nWidth
> 1 ) && ( nHeight
> 1 ) )
471 const Point
aCenter( rBound
.Center() );
472 const long nRadX
= aCenter
.X() - rBound
.Left();
473 const long nRadY
= aCenter
.Y() - rBound
.Top();
476 nPoints
= (sal_uInt16
) ( F_PI
* ( 1.5 * ( nRadX
+ nRadY
) -
477 sqrt( (double) labs( nRadX
* nRadY
) ) ) );
479 nPoints
= (sal_uInt16
) MinMax( nPoints
, 32, 256 );
481 if( ( nRadX
> 32 ) && ( nRadY
> 32 ) && ( nRadX
+ nRadY
) < 8192 )
485 const double fRadX
= nRadX
;
486 const double fRadY
= nRadY
;
487 const double fCenterX
= aCenter
.X();
488 const double fCenterY
= aCenter
.Y();
489 double fStart
= ImplGetParameter( aCenter
, rStart
, fRadX
, fRadY
);
490 double fEnd
= ImplGetParameter( aCenter
, rEnd
, fRadX
, fRadY
);
491 double fDiff
= fEnd
- fStart
;
502 // Proportionally shrink number of points( fDiff / (2PI) );
503 nPoints
= std::max( (sal_uInt16
) ( ( fDiff
* 0.1591549 ) * nPoints
), (sal_uInt16
) 16 );
504 fStep
= fDiff
/ ( nPoints
- 1 );
506 if( POLY_PIE
== eStyle
)
508 const Point
aCenter2( FRound( fCenterX
), FRound( fCenterY
) );
512 mpImplPolygon
= new ImplPolygon( nPoints
+ 2 );
513 mpImplPolygon
->mpPointAry
[ 0 ] = aCenter2
;
514 mpImplPolygon
->mpPointAry
[ nEnd
] = aCenter2
;
518 mpImplPolygon
= new ImplPolygon( ( POLY_CHORD
== eStyle
) ? ( nPoints
+ 1 ) : nPoints
);
523 for(; nStart
< nEnd
; nStart
++, fStart
+= fStep
)
525 Point
& rPt
= mpImplPolygon
->mpPointAry
[ nStart
];
527 rPt
.X() = FRound( fCenterX
+ fRadX
* cos( fStart
) );
528 rPt
.Y() = FRound( fCenterY
- fRadY
* sin( fStart
) );
531 if( POLY_CHORD
== eStyle
)
532 mpImplPolygon
->mpPointAry
[ nPoints
] = mpImplPolygon
->mpPointAry
[ 0 ];
535 mpImplPolygon
= (ImplPolygon
*) &aStaticImplPolygon
;
538 Polygon::Polygon( const Point
& rBezPt1
, const Point
& rCtrlPt1
,
539 const Point
& rBezPt2
, const Point
& rCtrlPt2
,
542 DBG_CTOR( Polygon
, NULL
);
544 nPoints
= ( 0 == nPoints
) ? 25 : ( ( nPoints
< 2 ) ? 2 : nPoints
);
546 const double fInc
= 1.0 / ( nPoints
- 1 );
547 double fK_1
= 0.0, fK1_1
= 1.0;
548 double fK_2
, fK_3
, fK1_2
, fK1_3
, fK12
, fK21
;
549 const double fX0
= rBezPt1
.X();
550 const double fY0
= rBezPt1
.Y();
551 const double fX1
= 3.0 * rCtrlPt1
.X();
552 const double fY1
= 3.0 * rCtrlPt1
.Y();
553 const double fX2
= 3.0 * rCtrlPt2
.X();
554 const double fY2
= 3.0 * rCtrlPt2
.Y();
555 const double fX3
= rBezPt2
.X();
556 const double fY3
= rBezPt2
.Y();
558 mpImplPolygon
= new ImplPolygon( nPoints
);
560 for( sal_uInt16 i
= 0; i
< nPoints
; i
++, fK_1
+= fInc
, fK1_1
-= fInc
)
562 Point
& rPt
= mpImplPolygon
->mpPointAry
[ i
];
564 fK_2
= fK_1
, fK_3
= ( fK_2
*= fK_1
), fK_3
*= fK_1
;
565 fK1_2
= fK1_1
, fK1_3
= ( fK1_2
*= fK1_1
), fK1_3
*= fK1_1
;
566 fK12
= fK_1
* fK1_2
, fK21
= fK_2
* fK1_1
;
568 rPt
.X() = FRound( fK1_3
* fX0
+ fK12
* fX1
+ fK21
* fX2
+ fK_3
* fX3
);
569 rPt
.Y() = FRound( fK1_3
* fY0
+ fK12
* fY1
+ fK21
* fY2
+ fK_3
* fY3
);
575 DBG_DTOR( Polygon
, NULL
);
577 // Remove if refcount == 0, otherwise decrement refcount
578 if ( mpImplPolygon
->mnRefCount
)
580 if ( mpImplPolygon
->mnRefCount
> 1 )
581 mpImplPolygon
->mnRefCount
--;
583 delete mpImplPolygon
;
587 const Point
* Polygon::GetConstPointAry() const
589 DBG_CHKTHIS( Polygon
, NULL
);
590 return (Point
*)mpImplPolygon
->mpPointAry
;
593 const sal_uInt8
* Polygon::GetConstFlagAry() const
595 DBG_CHKTHIS( Polygon
, NULL
);
596 return mpImplPolygon
->mpFlagAry
;
599 void Polygon::SetPoint( const Point
& rPt
, sal_uInt16 nPos
)
601 DBG_CHKTHIS( Polygon
, NULL
);
602 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
,
603 "Polygon::SetPoint(): nPos >= nPoints" );
606 mpImplPolygon
->mpPointAry
[nPos
] = rPt
;
609 void Polygon::SetFlags( sal_uInt16 nPos
, PolyFlags eFlags
)
611 DBG_CHKTHIS( Polygon
, NULL
);
612 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
,
613 "Polygon::SetFlags(): nPos >= nPoints" );
615 // we do only want to create the flag array if there
616 // is at least one flag different to POLY_NORMAL
617 if ( mpImplPolygon
|| ( eFlags
!= POLY_NORMAL
) )
620 mpImplPolygon
->ImplCreateFlagArray();
621 mpImplPolygon
->mpFlagAry
[ nPos
] = (sal_uInt8
) eFlags
;
625 const Point
& Polygon::GetPoint( sal_uInt16 nPos
) const
627 DBG_CHKTHIS( Polygon
, NULL
);
628 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
,
629 "Polygon::GetPoint(): nPos >= nPoints" );
631 return mpImplPolygon
->mpPointAry
[nPos
];
634 PolyFlags
Polygon::GetFlags( sal_uInt16 nPos
) const
636 DBG_CHKTHIS( Polygon
, NULL
);
637 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
,
638 "Polygon::GetFlags(): nPos >= nPoints" );
639 return( mpImplPolygon
->mpFlagAry
?
640 (PolyFlags
) mpImplPolygon
->mpFlagAry
[ nPos
] :
644 sal_Bool
Polygon::HasFlags() const
646 return mpImplPolygon
->mpFlagAry
!= NULL
;
649 sal_Bool
Polygon::IsRect() const
651 sal_Bool bIsRect
= sal_False
;
652 if ( mpImplPolygon
->mpFlagAry
== NULL
)
654 if ( ( ( mpImplPolygon
->mnPoints
== 5 ) && ( mpImplPolygon
->mpPointAry
[ 0 ] == mpImplPolygon
->mpPointAry
[ 4 ] ) ) ||
655 ( mpImplPolygon
->mnPoints
== 4 ) )
657 if ( ( mpImplPolygon
->mpPointAry
[ 0 ].X() == mpImplPolygon
->mpPointAry
[ 3 ].X() ) &&
658 ( mpImplPolygon
->mpPointAry
[ 0 ].Y() == mpImplPolygon
->mpPointAry
[ 1 ].Y() ) &&
659 ( mpImplPolygon
->mpPointAry
[ 1 ].X() == mpImplPolygon
->mpPointAry
[ 2 ].X() ) &&
660 ( mpImplPolygon
->mpPointAry
[ 2 ].Y() == mpImplPolygon
->mpPointAry
[ 3 ].Y() ) )
667 void Polygon::SetSize( sal_uInt16 nNewSize
)
669 DBG_CHKTHIS( Polygon
, NULL
);
671 if( nNewSize
!= mpImplPolygon
->mnPoints
)
674 mpImplPolygon
->ImplSetSize( nNewSize
);
678 sal_uInt16
Polygon::GetSize() const
680 DBG_CHKTHIS( Polygon
, NULL
);
682 return mpImplPolygon
->mnPoints
;
685 void Polygon::Clear()
687 DBG_CHKTHIS( Polygon
, NULL
);
689 if ( mpImplPolygon
->mnRefCount
)
691 if ( mpImplPolygon
->mnRefCount
> 1 )
692 mpImplPolygon
->mnRefCount
--;
694 delete mpImplPolygon
;
697 mpImplPolygon
= (ImplPolygon
*)(&aStaticImplPolygon
);
700 double Polygon::CalcDistance( sal_uInt16 nP1
, sal_uInt16 nP2
)
702 DBG_ASSERT( nP1
< mpImplPolygon
->mnPoints
,
703 "Polygon::CalcDistance(): nPos1 >= nPoints" );
704 DBG_ASSERT( nP2
< mpImplPolygon
->mnPoints
,
705 "Polygon::CalcDistance(): nPos2 >= nPoints" );
707 const Point
& rP1
= mpImplPolygon
->mpPointAry
[ nP1
];
708 const Point
& rP2
= mpImplPolygon
->mpPointAry
[ nP2
];
709 const double fDx
= rP2
.X() - rP1
.X();
710 const double fDy
= rP2
.Y() - rP1
.Y();
712 return sqrt( fDx
* fDx
+ fDy
* fDy
);
715 void Polygon::Optimize( sal_uIntPtr nOptimizeFlags
, const PolyOptimizeData
* pData
)
717 DBG_CHKTHIS( Polygon
, NULL
);
718 DBG_ASSERT( !mpImplPolygon
->mpFlagAry
, "Optimizing could fail with beziers!" );
720 sal_uInt16 nSize
= mpImplPolygon
->mnPoints
;
722 if( nOptimizeFlags
&& nSize
)
724 if( nOptimizeFlags
& POLY_OPTIMIZE_EDGES
)
726 const Rectangle
aBound( GetBoundRect() );
727 const double fArea
= ( aBound
.GetWidth() + aBound
.GetHeight() ) * 0.5;
728 const sal_uInt16 nPercent
= pData
? pData
->GetPercentValue() : 50;
730 Optimize( POLY_OPTIMIZE_NO_SAME
);
731 ImplReduceEdges( *this, fArea
, nPercent
);
733 else if( nOptimizeFlags
& ( POLY_OPTIMIZE_REDUCE
| POLY_OPTIMIZE_NO_SAME
) )
736 const Point
& rFirst
= mpImplPolygon
->mpPointAry
[ 0 ];
739 if( nOptimizeFlags
& ( POLY_OPTIMIZE_REDUCE
) )
740 nReduce
= pData
? pData
->GetAbsValue() : 4UL;
744 while( nSize
&& ( mpImplPolygon
->mpPointAry
[ nSize
- 1 ] == rFirst
) )
749 sal_uInt16 nLast
= 0, nNewCount
= 1;
751 aNewPoly
.SetSize( nSize
);
752 aNewPoly
[ 0 ] = rFirst
;
754 for( sal_uInt16 i
= 1; i
< nSize
; i
++ )
756 if( ( mpImplPolygon
->mpPointAry
[ i
] != mpImplPolygon
->mpPointAry
[ nLast
] ) &&
757 ( !nReduce
|| ( nReduce
< (sal_uIntPtr
) FRound( CalcDistance( nLast
, i
) ) ) ) )
759 aNewPoly
[ nNewCount
++ ] = mpImplPolygon
->mpPointAry
[ nLast
= i
];
766 aNewPoly
.SetSize( nNewCount
);
772 nSize
= mpImplPolygon
->mnPoints
;
776 if( ( nOptimizeFlags
& POLY_OPTIMIZE_CLOSE
) &&
777 ( mpImplPolygon
->mpPointAry
[ 0 ] != mpImplPolygon
->mpPointAry
[ nSize
- 1 ] ) )
779 SetSize( mpImplPolygon
->mnPoints
+ 1 );
780 mpImplPolygon
->mpPointAry
[ mpImplPolygon
->mnPoints
- 1 ] = mpImplPolygon
->mpPointAry
[ 0 ];
782 else if( ( nOptimizeFlags
& POLY_OPTIMIZE_OPEN
) &&
783 ( mpImplPolygon
->mpPointAry
[ 0 ] == mpImplPolygon
->mpPointAry
[ nSize
- 1 ] ) )
785 const Point
& rFirst
= mpImplPolygon
->mpPointAry
[ 0 ];
787 while( nSize
&& ( mpImplPolygon
->mpPointAry
[ nSize
- 1 ] == rFirst
) )
797 /** Recursively subdivide cubic bezier curve via deCasteljau.
800 Output iterator, where the subdivided polylines are written to.
803 Squared difference of curve to a straight line
806 Exactly four points, interpreted as support and control points of
807 a cubic bezier curve. Must be in device coordinates, since stop
808 criterion is based on the following assumption: the device has a
809 finite resolution, it is thus sufficient to stop subdivision if the
810 curve does not deviate more than one pixel from a straight line.
813 static void ImplAdaptiveSubdivide( ::std::back_insert_iterator
< ::std::vector
< Point
> >& rPointIter
,
817 const double P1x
, const double P1y
,
818 const double P2x
, const double P2y
,
819 const double P3x
, const double P3y
,
820 const double P4x
, const double P4y
)
822 // Hard limit on recursion depth, empiric number.
823 enum {maxRecursionDepth
=128};
825 // Perform bezier flatness test (lecture notes from R. Schaback,
826 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
828 // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
831 // What is calculated here is an upper bound to the distance from
832 // a line through b_0 and b_3 (P1 and P4 in our notation) and the
833 // curve. We can drop 0 and n from the running indices, since the
834 // argument of max becomes zero for those cases.
835 const double fJ1x( P2x
- P1x
- 1.0/3.0*(P4x
- P1x
) );
836 const double fJ1y( P2y
- P1y
- 1.0/3.0*(P4y
- P1y
) );
837 const double fJ2x( P3x
- P1x
- 2.0/3.0*(P4x
- P1x
) );
838 const double fJ2y( P3y
- P1y
- 2.0/3.0*(P4y
- P1y
) );
839 const double distance2( ::std::max( fJ1x
*fJ1x
+ fJ1y
*fJ1y
,
840 fJ2x
*fJ2x
+ fJ2y
*fJ2y
) );
842 // stop if error measure does not improve anymore. This is a
843 // safety guard against floating point inaccuracies.
844 // stop at recursion level 128. This is a safety guard against
845 // floating point inaccuracies.
846 // stop if distance from line is guaranteed to be bounded by d
848 recursionDepth
< maxRecursionDepth
&&
851 // deCasteljau bezier arc, split at t=0.5
852 // Foley/vanDam, p. 508
853 const double L1x( P1x
), L1y( P1y
);
854 const double L2x( (P1x
+ P2x
)*0.5 ), L2y( (P1y
+ P2y
)*0.5 );
855 const double Hx ( (P2x
+ P3x
)*0.5 ), Hy ( (P2y
+ P3y
)*0.5 );
856 const double L3x( (L2x
+ Hx
)*0.5 ), L3y( (L2y
+ Hy
)*0.5 );
857 const double R4x( P4x
), R4y( P4y
);
858 const double R3x( (P3x
+ P4x
)*0.5 ), R3y( (P3y
+ P4y
)*0.5 );
859 const double R2x( (Hx
+ R3x
)*0.5 ), R2y( (Hy
+ R3y
)*0.5 );
860 const double R1x( (L3x
+ R2x
)*0.5 ), R1y( (L3y
+ R2y
)*0.5 );
861 const double L4x( R1x
), L4y( R1y
);
865 ImplAdaptiveSubdivide(rPointIter
, distance2
, recursionDepth
, d2
, L1x
, L1y
, L2x
, L2y
, L3x
, L3y
, L4x
, L4y
);
866 ImplAdaptiveSubdivide(rPointIter
, distance2
, recursionDepth
, d2
, R1x
, R1y
, R2x
, R2y
, R3x
, R3y
, R4x
, R4y
);
870 // requested resolution reached.
871 // Add end points to output iterator.
872 // order is preserved, since this is so to say depth first traversal.
873 *rPointIter
++ = Point( FRound(P1x
), FRound(P1y
) );
877 void Polygon::AdaptiveSubdivide( Polygon
& rResult
, const double d
) const
879 if( !mpImplPolygon
->mpFlagAry
)
886 sal_uInt16
nPts( GetSize() );
887 ::std::vector
< Point
> aPoints
;
888 aPoints
.reserve( nPts
);
889 ::std::back_insert_iterator
< ::std::vector
< Point
> > aPointIter( aPoints
);
893 if( ( i
+ 3 ) < nPts
)
895 sal_uInt8
P1( mpImplPolygon
->mpFlagAry
[ i
] );
896 sal_uInt8
P4( mpImplPolygon
->mpFlagAry
[ i
+ 3 ] );
898 if( ( POLY_NORMAL
== P1
|| POLY_SMOOTH
== P1
|| POLY_SYMMTR
== P1
) &&
899 ( POLY_CONTROL
== mpImplPolygon
->mpFlagAry
[ i
+ 1 ] ) &&
900 ( POLY_CONTROL
== mpImplPolygon
->mpFlagAry
[ i
+ 2 ] ) &&
901 ( POLY_NORMAL
== P4
|| POLY_SMOOTH
== P4
|| POLY_SYMMTR
== P4
) )
903 ImplAdaptiveSubdivide( aPointIter
, d
*d
+1.0, 0, d
*d
,
904 mpImplPolygon
->mpPointAry
[ i
].X(), mpImplPolygon
->mpPointAry
[ i
].Y(),
905 mpImplPolygon
->mpPointAry
[ i
+1 ].X(), mpImplPolygon
->mpPointAry
[ i
+1 ].Y(),
906 mpImplPolygon
->mpPointAry
[ i
+2 ].X(), mpImplPolygon
->mpPointAry
[ i
+2 ].Y(),
907 mpImplPolygon
->mpPointAry
[ i
+3 ].X(), mpImplPolygon
->mpPointAry
[ i
+3 ].Y() );
913 *aPointIter
++ = mpImplPolygon
->mpPointAry
[ i
++ ];
915 if (aPoints
.size() >= SAL_MAX_UINT16
)
917 OSL_ENSURE(aPoints
.size() < SAL_MAX_UINT16
,
918 "Polygon::AdapativeSubdivision created polygon too many points;"
919 " using original polygon instead");
921 // The resulting polygon can not hold all the points
922 // that we have created so far. Stop the subdivision
923 // and return a copy of the unmodified polygon.
929 // fill result polygon
930 rResult
= Polygon( (sal_uInt16
)aPoints
.size() ); // ensure sufficient size for copy
931 ::std::copy(aPoints
.begin(), aPoints
.end(), rResult
.mpImplPolygon
->mpPointAry
);
935 void Polygon::ImplReduceEdges( Polygon
& rPoly
, const double& rArea
, sal_uInt16 nPercent
)
937 const double fBound
= 2000.0 * ( 100 - nPercent
) * 0.01;
938 sal_uInt16 nNumNoChange
= 0,
941 while( nNumNoChange
< 2 )
943 sal_uInt16 nPntCnt
= rPoly
.GetSize(), nNewPos
= 0;
944 Polygon
aNewPoly( nPntCnt
);
945 sal_Bool bChangeInThisRun
= sal_False
;
947 for( sal_uInt16 n
= 0; n
< nPntCnt
; n
++ )
949 sal_Bool bDeletePoint
= sal_False
;
951 if( ( n
+ nNumRuns
) % 2 )
953 sal_uInt16 nIndPrev
= !n
? nPntCnt
- 1 : n
- 1;
954 sal_uInt16 nIndPrevPrev
= !nIndPrev
? nPntCnt
- 1 : nIndPrev
- 1;
955 sal_uInt16 nIndNext
= ( n
== nPntCnt
-1 ) ? 0 : n
+ 1;
956 sal_uInt16 nIndNextNext
= ( nIndNext
== nPntCnt
- 1 ) ? 0 : nIndNext
+ 1;
957 Vector2D
aVec1( rPoly
[ nIndPrev
] ); aVec1
-= rPoly
[ nIndPrevPrev
];
958 Vector2D
aVec2( rPoly
[ n
] ); aVec2
-= rPoly
[ nIndPrev
];
959 Vector2D
aVec3( rPoly
[ nIndNext
] ); aVec3
-= rPoly
[ n
];
960 Vector2D
aVec4( rPoly
[ nIndNextNext
] ); aVec4
-= rPoly
[ nIndNext
];
961 double fDist1
= aVec1
.GetLength(), fDist2
= aVec2
.GetLength();
962 double fDist3
= aVec3
.GetLength(), fDist4
= aVec4
.GetLength();
963 double fTurnB
= aVec2
.Normalize().Scalar( aVec3
.Normalize() );
965 if( fabs( fTurnB
) < ( 1.0 + SMALL_DVALUE
) && fabs( fTurnB
) > ( 1.0 - SMALL_DVALUE
) )
966 bDeletePoint
= sal_True
;
969 Vector2D
aVecB( rPoly
[ nIndNext
] );
970 double fDistB
= ( aVecB
-= rPoly
[ nIndPrev
] ).GetLength();
971 double fLenWithB
= fDist2
+ fDist3
;
972 double fLenFact
= ( fDistB
!= 0.0 ) ? fLenWithB
/ fDistB
: 1.0;
973 double fTurnPrev
= aVec1
.Normalize().Scalar( aVec2
);
974 double fTurnNext
= aVec3
.Scalar( aVec4
.Normalize() );
975 double fGradPrev
, fGradB
, fGradNext
;
977 if( fabs( fTurnPrev
) < ( 1.0 + SMALL_DVALUE
) && fabs( fTurnPrev
) > ( 1.0 - SMALL_DVALUE
) )
980 fGradPrev
= acos( fTurnPrev
) / ( aVec1
.IsNegative( aVec2
) ? -F_PI180
: F_PI180
);
982 fGradB
= acos( fTurnB
) / ( aVec2
.IsNegative( aVec3
) ? -F_PI180
: F_PI180
);
984 if( fabs( fTurnNext
) < ( 1.0 + SMALL_DVALUE
) && fabs( fTurnNext
) > ( 1.0 - SMALL_DVALUE
) )
987 fGradNext
= acos( fTurnNext
) / ( aVec3
.IsNegative( aVec4
) ? -F_PI180
: F_PI180
);
989 if( ( fGradPrev
> 0.0 && fGradB
< 0.0 && fGradNext
> 0.0 ) ||
990 ( fGradPrev
< 0.0 && fGradB
> 0.0 && fGradNext
< 0.0 ) )
992 if( ( fLenFact
< ( FSQRT2
+ SMALL_DVALUE
) ) &&
993 ( ( ( fDist1
+ fDist4
) / ( fDist2
+ fDist3
) ) * 2000.0 ) > fBound
)
995 bDeletePoint
= sal_True
;
1000 double fRelLen
= 1.0 - sqrt( fDistB
/ rArea
);
1004 else if( fRelLen
> 1.0 )
1007 if( ( (sal_uInt32
) ( ( ( fLenFact
- 1.0 ) * 1000000.0 ) + 0.5 ) < fBound
) &&
1008 ( fabs( fGradB
) <= ( fRelLen
* fBound
* 0.01 ) ) )
1010 bDeletePoint
= sal_True
;
1017 aNewPoly
[ nNewPos
++ ] = rPoly
[ n
];
1019 bChangeInThisRun
= sal_True
;
1022 if( bChangeInThisRun
&& nNewPos
)
1024 aNewPoly
.SetSize( nNewPos
);
1035 void Polygon::Move( long nHorzMove
, long nVertMove
)
1037 DBG_CHKTHIS( Polygon
, NULL
);
1039 // This check is required for DrawEngine
1040 if ( !nHorzMove
&& !nVertMove
)
1046 sal_uInt16 nCount
= mpImplPolygon
->mnPoints
;
1047 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
1049 Point
* pPt
= &(mpImplPolygon
->mpPointAry
[i
]);
1050 pPt
->X() += nHorzMove
;
1051 pPt
->Y() += nVertMove
;
1055 void Polygon::Translate(const Point
& rTrans
)
1057 DBG_CHKTHIS( Polygon
, NULL
);
1060 for ( sal_uInt16 i
= 0, nCount
= mpImplPolygon
->mnPoints
; i
< nCount
; i
++ )
1061 mpImplPolygon
->mpPointAry
[ i
] += rTrans
;
1064 void Polygon::Scale( double fScaleX
, double fScaleY
)
1066 DBG_CHKTHIS( Polygon
, NULL
);
1069 for ( sal_uInt16 i
= 0, nCount
= mpImplPolygon
->mnPoints
; i
< nCount
; i
++ )
1071 Point
& rPnt
= mpImplPolygon
->mpPointAry
[i
];
1072 rPnt
.X() = (long) ( fScaleX
* rPnt
.X() );
1073 rPnt
.Y() = (long) ( fScaleY
* rPnt
.Y() );
1077 void Polygon::Rotate( const Point
& rCenter
, sal_uInt16 nAngle10
)
1079 DBG_CHKTHIS( Polygon
, NULL
);
1084 const double fAngle
= F_PI1800
* nAngle10
;
1085 Rotate( rCenter
, sin( fAngle
), cos( fAngle
) );
1089 void Polygon::Rotate( const Point
& rCenter
, double fSin
, double fCos
)
1091 DBG_CHKTHIS( Polygon
, NULL
);
1095 long nCenterX
= rCenter
.X();
1096 long nCenterY
= rCenter
.Y();
1098 for( sal_uInt16 i
= 0, nCount
= mpImplPolygon
->mnPoints
; i
< nCount
; i
++ )
1100 Point
& rPt
= mpImplPolygon
->mpPointAry
[ i
];
1102 nX
= rPt
.X() - nCenterX
;
1103 nY
= rPt
.Y() - nCenterY
;
1104 rPt
.X() = (long) FRound( fCos
* nX
+ fSin
* nY
) + nCenterX
;
1105 rPt
.Y() = -(long) FRound( fSin
* nX
- fCos
* nY
) + nCenterY
;
1109 class ImplPointFilter
1112 virtual void LastPoint() = 0;
1113 virtual void Input( const Point
& rPoint
) = 0;
1116 ~ImplPointFilter() {}
1119 class ImplPolygonPointFilter
: public ImplPointFilter
1122 ImplPolygon
* mpPoly
; // Don't remove, assigned by polygon
1125 ImplPolygonPointFilter( sal_uInt16 nDestSize
) :
1128 mpPoly
= new ImplPolygon( nDestSize
);
1131 virtual ~ImplPolygonPointFilter() {}
1133 virtual void LastPoint();
1134 virtual void Input( const Point
& rPoint
);
1137 void ImplPolygonPointFilter::Input( const Point
& rPoint
)
1139 if ( !mnSize
|| (rPoint
!= mpPoly
->mpPointAry
[mnSize
-1]) )
1142 if ( mnSize
> mpPoly
->mnPoints
)
1143 mpPoly
->ImplSetSize( mnSize
);
1144 mpPoly
->mpPointAry
[mnSize
-1] = rPoint
;
1148 void ImplPolygonPointFilter::LastPoint()
1150 if ( mnSize
< mpPoly
->mnPoints
)
1151 mpPoly
->ImplSetSize( mnSize
);
1154 class ImplEdgePointFilter
: public ImplPointFilter
1158 ImplPointFilter
& mrNextFilter
;
1166 ImplEdgePointFilter( int nEdge
, long nLow
, long nHigh
,
1167 ImplPointFilter
& rNextFilter
) :
1168 mrNextFilter( rNextFilter
),
1176 virtual ~ImplEdgePointFilter() {}
1178 Point
EdgeSection( const Point
& rPoint
, int nEdge
) const;
1179 int VisibleSide( const Point
& rPoint
) const;
1180 int IsPolygon() const
1181 { return maFirstPoint
== maLastPoint
; }
1183 virtual void Input( const Point
& rPoint
);
1184 virtual void LastPoint();
1187 inline int ImplEdgePointFilter::VisibleSide( const Point
& rPoint
) const
1189 if ( mnEdge
& EDGE_HORZ
)
1191 return rPoint
.X() < mnLow
? EDGE_LEFT
:
1192 rPoint
.X() > mnHigh
? EDGE_RIGHT
: 0;
1196 return rPoint
.Y() < mnLow
? EDGE_TOP
:
1197 rPoint
.Y() > mnHigh
? EDGE_BOTTOM
: 0;
1201 Point
ImplEdgePointFilter::EdgeSection( const Point
& rPoint
, int nEdge
) const
1203 long lx
= maLastPoint
.X();
1204 long ly
= maLastPoint
.Y();
1205 long md
= rPoint
.X() - lx
;
1206 long mn
= rPoint
.Y() - ly
;
1210 if ( nEdge
& EDGE_VERT
)
1212 nNewY
= (nEdge
== EDGE_TOP
) ? mnLow
: mnHigh
;
1213 long dy
= nNewY
- ly
;
1216 else if ( (LONG_MAX
/ std::abs(md
)) >= std::abs(dy
) )
1217 nNewX
= (dy
* md
) / mn
+ lx
;
1233 nNewX
= (long)ady
+ lx
;
1238 nNewX
= (nEdge
== EDGE_LEFT
) ? mnLow
: mnHigh
;
1239 long dx
= nNewX
- lx
;
1242 else if ( (LONG_MAX
/ std::abs(mn
)) >= std::abs(dx
) )
1243 nNewY
= (dx
* mn
) / md
+ ly
;
1259 nNewY
= (long)adx
+ ly
;
1263 return Point( nNewX
, nNewY
);
1266 void ImplEdgePointFilter::Input( const Point
& rPoint
)
1268 int nOutside
= VisibleSide( rPoint
);
1272 maFirstPoint
= rPoint
;
1273 mbFirst
= sal_False
;
1275 mrNextFilter
.Input( rPoint
);
1277 else if ( rPoint
== maLastPoint
)
1279 else if ( !nOutside
)
1281 if ( mnLastOutside
)
1282 mrNextFilter
.Input( EdgeSection( rPoint
, mnLastOutside
) );
1283 mrNextFilter
.Input( rPoint
);
1285 else if ( !mnLastOutside
)
1286 mrNextFilter
.Input( EdgeSection( rPoint
, nOutside
) );
1287 else if ( nOutside
!= mnLastOutside
)
1289 mrNextFilter
.Input( EdgeSection( rPoint
, mnLastOutside
) );
1290 mrNextFilter
.Input( EdgeSection( rPoint
, nOutside
) );
1293 maLastPoint
= rPoint
;
1294 mnLastOutside
= nOutside
;
1297 void ImplEdgePointFilter::LastPoint()
1301 int nOutside
= VisibleSide( maFirstPoint
);
1303 if ( nOutside
!= mnLastOutside
)
1304 Input( maFirstPoint
);
1305 mrNextFilter
.LastPoint();
1309 void Polygon::Clip( const Rectangle
& rRect
, sal_Bool bPolygon
)
1311 // #105251# Justify rect befor edge filtering
1312 Rectangle
aJustifiedRect( rRect
);
1313 aJustifiedRect
.Justify();
1315 sal_uInt16 nSourceSize
= mpImplPolygon
->mnPoints
;
1316 ImplPolygonPointFilter
aPolygon( nSourceSize
);
1317 ImplEdgePointFilter
aHorzFilter( EDGE_HORZ
, aJustifiedRect
.Left(), aJustifiedRect
.Right(),
1319 ImplEdgePointFilter
aVertFilter( EDGE_VERT
, aJustifiedRect
.Top(), aJustifiedRect
.Bottom(),
1322 for ( sal_uInt16 i
= 0; i
< nSourceSize
; i
++ )
1323 aVertFilter
.Input( mpImplPolygon
->mpPointAry
[i
] );
1324 if ( bPolygon
|| aVertFilter
.IsPolygon() )
1325 aVertFilter
.LastPoint();
1327 aPolygon
.LastPoint();
1329 // Delete old ImpPolygon-data and assign from ImpPolygonPointFilter
1330 if ( mpImplPolygon
->mnRefCount
)
1332 if ( mpImplPolygon
->mnRefCount
> 1 )
1333 mpImplPolygon
->mnRefCount
--;
1335 delete mpImplPolygon
;
1337 mpImplPolygon
= aPolygon
.mpPoly
;
1340 Rectangle
Polygon::GetBoundRect() const
1342 DBG_CHKTHIS( Polygon
, NULL
);
1343 // Removing the assert. Bezier curves have the attribute that each single
1344 // curve segment defined by four points can not exit the four-point polygon
1345 // defined by that points. This allows to say that the curve segment can also
1346 // never leave the Range of it's defining points.
1347 // The result is that Polygon::GetBoundRect() may not create the minimal
1348 // BoundRect of the Polygon (to get that, use basegfx::B2DPolygon classes),
1349 // but will always create a valid BoundRect, at least as long as this method
1350 // 'blindly' travels over all points, including control points.
1352 // DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetBoundRect could fail with beziers!" );
1354 sal_uInt16 nCount
= mpImplPolygon
->mnPoints
;
1358 long nXMin
, nXMax
, nYMin
, nYMax
;
1360 const Point
* pPt
= &(mpImplPolygon
->mpPointAry
[0]);
1361 nXMin
= nXMax
= pPt
->X();
1362 nYMin
= nYMax
= pPt
->Y();
1364 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
1366 pPt
= &(mpImplPolygon
->mpPointAry
[i
]);
1368 if ( pPt
->X() < nXMin
)
1370 if ( pPt
->X() > nXMax
)
1372 if ( pPt
->Y() < nYMin
)
1374 if ( pPt
->Y() > nYMax
)
1378 return Rectangle( nXMin
, nYMin
, nXMax
, nYMax
);
1381 double Polygon::GetSignedArea() const
1383 DBG_CHKTHIS( Polygon
, NULL
);
1384 DBG_ASSERT( !mpImplPolygon
->mpFlagAry
, "GetArea could fail with beziers!" );
1388 if( mpImplPolygon
->mnPoints
> 2 )
1390 const sal_uInt16 nCount1
= mpImplPolygon
->mnPoints
- 1;
1392 for( sal_uInt16 i
= 0; i
< nCount1
; )
1394 const Point
& rPt
= mpImplPolygon
->mpPointAry
[ i
];
1395 const Point
& rPt1
= mpImplPolygon
->mpPointAry
[ ++i
];
1396 fArea
+= ( rPt
.X() - rPt1
.X() ) * ( rPt
.Y() + rPt1
.Y() );
1399 const Point
& rPt
= mpImplPolygon
->mpPointAry
[ nCount1
];
1400 const Point
& rPt0
= mpImplPolygon
->mpPointAry
[ 0 ];
1401 fArea
+= ( rPt
.X() - rPt0
.X() ) * ( rPt
.Y() + rPt0
.Y() );
1407 sal_Bool
Polygon::IsInside( const Point
& rPoint
) const
1409 DBG_CHKTHIS( Polygon
, NULL
);
1410 DBG_ASSERT( !mpImplPolygon
->mpFlagAry
, "IsInside could fail with beziers!" );
1412 const Rectangle
aBound( GetBoundRect() );
1413 const Line
aLine( rPoint
, Point( aBound
.Right() + 100L, rPoint
.Y() ) );
1414 sal_uInt16 nCount
= mpImplPolygon
->mnPoints
;
1415 sal_uInt16 nPCounter
= 0;
1417 if ( ( nCount
> 2 ) && aBound
.IsInside( rPoint
) )
1419 Point
aPt1( mpImplPolygon
->mpPointAry
[ 0 ] );
1420 Point aIntersection
;
1421 Point aLastIntersection
;
1423 while ( ( aPt1
== mpImplPolygon
->mpPointAry
[ nCount
- 1 ] ) && ( nCount
> 3 ) )
1426 for ( sal_uInt16 i
= 1; i
<= nCount
; i
++ )
1428 const Point
& rPt2
= mpImplPolygon
->mpPointAry
[ ( i
< nCount
) ? i
: 0 ];
1430 if ( aLine
.Intersection( Line( aPt1
, rPt2
), aIntersection
) )
1432 // This avoids insertion of double intersections
1435 if ( aIntersection
!= aLastIntersection
)
1437 aLastIntersection
= aIntersection
;
1443 aLastIntersection
= aIntersection
;
1452 // is inside, if number of intersection points is odd
1453 return ( ( nPCounter
& 1 ) == 1 );
1456 sal_Bool
Polygon::IsRightOrientated() const
1458 DBG_CHKTHIS( Polygon
, NULL
);
1459 return GetSignedArea() >= 0.0;
1462 void Polygon::Insert( sal_uInt16 nPos
, const Point
& rPt
, PolyFlags eFlags
)
1464 DBG_CHKTHIS( Polygon
, NULL
);
1467 if( nPos
>= mpImplPolygon
->mnPoints
)
1468 nPos
= mpImplPolygon
->mnPoints
;
1470 mpImplPolygon
->ImplSplit( nPos
, 1 );
1471 mpImplPolygon
->mpPointAry
[ nPos
] = rPt
;
1473 if( POLY_NORMAL
!= eFlags
)
1475 mpImplPolygon
->ImplCreateFlagArray();
1476 mpImplPolygon
->mpFlagAry
[ nPos
] = (sal_uInt8
) eFlags
;
1480 void Polygon::Insert( sal_uInt16 nPos
, const Polygon
& rPoly
)
1482 DBG_CHKTHIS( Polygon
, NULL
);
1483 const sal_uInt16 nInsertCount
= rPoly
.mpImplPolygon
->mnPoints
;
1489 if( nPos
>= mpImplPolygon
->mnPoints
)
1490 nPos
= mpImplPolygon
->mnPoints
;
1492 if( rPoly
.mpImplPolygon
->mpFlagAry
)
1493 mpImplPolygon
->ImplCreateFlagArray();
1495 mpImplPolygon
->ImplSplit( nPos
, nInsertCount
, rPoly
.mpImplPolygon
);
1499 Point
& Polygon::operator[]( sal_uInt16 nPos
)
1501 DBG_CHKTHIS( Polygon
, NULL
);
1502 DBG_ASSERT( nPos
< mpImplPolygon
->mnPoints
, "Polygon::[]: nPos >= nPoints" );
1505 return mpImplPolygon
->mpPointAry
[nPos
];
1508 Polygon
& Polygon::operator=( const Polygon
& rPoly
)
1510 DBG_CHKTHIS( Polygon
, NULL
);
1511 DBG_CHKOBJ( &rPoly
, Polygon
, NULL
);
1512 DBG_ASSERT( rPoly
.mpImplPolygon
->mnRefCount
< 0xFFFFFFFE, "Polygon: RefCount overflow" );
1514 // Increase refcounter before assigning
1515 // Note: RefCount == 0 for static objects
1516 if ( rPoly
.mpImplPolygon
->mnRefCount
)
1517 rPoly
.mpImplPolygon
->mnRefCount
++;
1519 // Delete if recount == 0, otherwise decrement
1520 if ( mpImplPolygon
->mnRefCount
)
1522 if ( mpImplPolygon
->mnRefCount
> 1 )
1523 mpImplPolygon
->mnRefCount
--;
1525 delete mpImplPolygon
;
1528 mpImplPolygon
= rPoly
.mpImplPolygon
;
1532 sal_Bool
Polygon::operator==( const Polygon
& rPoly
) const
1534 DBG_CHKTHIS( Polygon
, NULL
);
1535 DBG_CHKOBJ( &rPoly
, Polygon
, NULL
);
1537 if ( (rPoly
.mpImplPolygon
== mpImplPolygon
) )
1543 sal_Bool
Polygon::IsEqual( const Polygon
& rPoly
) const
1545 sal_Bool bIsEqual
= sal_True
;
1547 if ( GetSize() != rPoly
.GetSize() )
1548 bIsEqual
= sal_False
;
1551 for ( i
= 0; i
< GetSize(); i
++ )
1553 if ( ( GetPoint( i
) != rPoly
.GetPoint( i
) ) ||
1554 ( GetFlags( i
) != rPoly
.GetFlags( i
) ) )
1556 bIsEqual
= sal_False
;
1564 SvStream
& operator>>( SvStream
& rIStream
, Polygon
& rPoly
)
1566 DBG_CHKOBJ( &rPoly
, Polygon
, NULL
);
1567 DBG_ASSERTWARNING( rIStream
.GetVersion(), "Polygon::>> - Solar-Version not set on rIStream" );
1570 sal_uInt16
nPoints(0);
1572 // read all points and create array
1573 rIStream
>> nPoints
;
1574 if ( rPoly
.mpImplPolygon
->mnRefCount
!= 1 )
1576 if ( rPoly
.mpImplPolygon
->mnRefCount
)
1577 rPoly
.mpImplPolygon
->mnRefCount
--;
1578 rPoly
.mpImplPolygon
= new ImplPolygon( nPoints
);
1581 rPoly
.mpImplPolygon
->ImplSetSize( nPoints
, sal_False
);
1584 // Determine whether we need to write through operators
1585 #if (SAL_TYPES_SIZEOFLONG) != 4
1588 #ifdef OSL_BIGENDIAN
1589 if ( rIStream
.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN
)
1591 if ( rIStream
.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN
)
1595 for( i
= 0; i
< nPoints
; i
++ )
1597 //fdo#39428 SvStream no longer supports operator>>(long&)
1598 sal_Int32
nTmpX(0), nTmpY(0);
1599 rIStream
>> nTmpX
>> nTmpY
;
1600 rPoly
.mpImplPolygon
->mpPointAry
[i
].X() = nTmpX
;
1601 rPoly
.mpImplPolygon
->mpPointAry
[i
].Y() = nTmpY
;
1605 rIStream
.Read( rPoly
.mpImplPolygon
->mpPointAry
, nPoints
*sizeof(Point
) );
1611 SvStream
& operator<<( SvStream
& rOStream
, const Polygon
& rPoly
)
1613 DBG_CHKOBJ( &rPoly
, Polygon
, NULL
);
1614 DBG_ASSERTWARNING( rOStream
.GetVersion(), "Polygon::<< - Solar-Version not set on rOStream" );
1617 sal_uInt16 nPoints
= rPoly
.GetSize();
1619 // Write number of points
1620 rOStream
<< nPoints
;
1623 // Determine whether we need to write through operators
1624 #if (SAL_TYPES_SIZEOFLONG) != 4
1627 #ifdef OSL_BIGENDIAN
1628 if ( rOStream
.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN
)
1630 if ( rOStream
.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN
)
1634 for( i
= 0; i
< nPoints
; i
++ )
1636 //fdo#39428 SvStream no longer supports operator<<(long)
1637 rOStream
<< sal::static_int_cast
<sal_Int32
>( rPoly
.mpImplPolygon
->mpPointAry
[i
].X() )
1638 << sal::static_int_cast
<sal_Int32
>( rPoly
.mpImplPolygon
->mpPointAry
[i
].Y() );
1644 rOStream
.Write( rPoly
.mpImplPolygon
->mpPointAry
, nPoints
*sizeof(Point
) );
1651 void Polygon::ImplRead( SvStream
& rIStream
)
1653 sal_uInt8
bHasPolyFlags(0);
1658 if ( bHasPolyFlags
)
1660 mpImplPolygon
->mpFlagAry
= new sal_uInt8
[ mpImplPolygon
->mnPoints
];
1661 rIStream
.Read( mpImplPolygon
->mpFlagAry
, mpImplPolygon
->mnPoints
);
1665 void Polygon::Read( SvStream
& rIStream
)
1667 VersionCompat
aCompat( rIStream
, STREAM_READ
);
1669 ImplRead( rIStream
);
1672 void Polygon::ImplWrite( SvStream
& rOStream
) const
1674 sal_uInt8 bHasPolyFlags
= mpImplPolygon
->mpFlagAry
!= NULL
;
1678 if ( bHasPolyFlags
)
1679 rOStream
.Write( mpImplPolygon
->mpFlagAry
, mpImplPolygon
->mnPoints
);
1682 void Polygon::Write( SvStream
& rOStream
) const
1684 VersionCompat
aCompat( rOStream
, STREAM_WRITE
, 1 );
1686 ImplWrite( rOStream
);
1689 // #i74631#/#i115917# numerical correction method for B2DPolygon
1690 void impCorrectContinuity(basegfx::B2DPolygon
& roPolygon
, sal_uInt32 nIndex
, sal_uInt8 nCFlag
)
1692 const sal_uInt32
nPointCount(roPolygon
.count());
1693 OSL_ENSURE(nIndex
< nPointCount
, "impCorrectContinuity: index access out of range (!)");
1695 if(nIndex
< nPointCount
&& (POLY_SMOOTH
== nCFlag
|| POLY_SYMMTR
== nCFlag
))
1697 if(roPolygon
.isPrevControlPointUsed(nIndex
) && roPolygon
.isNextControlPointUsed(nIndex
))
1699 // #i115917# Patch from osnola (modified, thanks for showing the porblem)
1701 // The correction is needed because an integer polygon with control points
1702 // is converted to double precision. When C1 or C2 is used the involved vectors
1703 // may not have the same directions/lengths since these come from integer coordinates
1704 // and may have been snapped to different nearest integer coordinates. The snap error
1705 // is in the range of +-1 in y and y, thus 0.0 <= error <= sqrt(2.0). Nonetheless,
1706 // it needs to be corrected to be able to detect the continuity in this points
1709 // We only have the integer data here (already in double precision form, but no mantisses
1710 // used), so the best correction is to use:
1712 // for C1: The longest vector since it potentially has best preserved the original vector.
1713 // Even better the sum of the vectors, weighted by their length. This gives the
1714 // normal vector addition to get the vector itself, lengths need to be preserved.
1715 // for C2: The mediated vector(s) since both should be the same, but mirrored
1717 // extract the point and vectors
1718 const basegfx::B2DPoint
aPoint(roPolygon
.getB2DPoint(nIndex
));
1719 const basegfx::B2DVector
aNext(roPolygon
.getNextControlPoint(nIndex
) - aPoint
);
1720 const basegfx::B2DVector
aPrev(aPoint
- roPolygon
.getPrevControlPoint(nIndex
));
1722 // calculate common direction vector, normalize
1723 const basegfx::B2DVector
aDirection(aNext
+ aPrev
);
1725 if(POLY_SMOOTH
== nCFlag
)
1727 // C1: apply common direction vector, preserve individual lengths
1728 const double fInvDirectionLen(1.0 / aDirection
.getLength());
1729 roPolygon
.setNextControlPoint(nIndex
, basegfx::B2DPoint(aPoint
+ (aDirection
* (aNext
.getLength() * fInvDirectionLen
))));
1730 roPolygon
.setPrevControlPoint(nIndex
, basegfx::B2DPoint(aPoint
- (aDirection
* (aPrev
.getLength() * fInvDirectionLen
))));
1734 // C2: get mediated length. Taking half of the unnormalized direction would be
1735 // an approximation, but not correct.
1736 const double fMedLength((aNext
.getLength() + aPrev
.getLength()) * (0.5 / aDirection
.getLength()));
1737 const basegfx::B2DVector
aScaledDirection(aDirection
* fMedLength
);
1739 // Bring Direction to correct length and apply
1740 roPolygon
.setNextControlPoint(nIndex
, basegfx::B2DPoint(aPoint
+ aScaledDirection
));
1741 roPolygon
.setPrevControlPoint(nIndex
, basegfx::B2DPoint(aPoint
- aScaledDirection
));
1747 // convert to basegfx::B2DPolygon and return
1748 basegfx::B2DPolygon
Polygon::getB2DPolygon() const
1750 basegfx::B2DPolygon aRetval
;
1751 const sal_uInt16
nCount(mpImplPolygon
->mnPoints
);
1755 if(mpImplPolygon
->mpFlagAry
)
1757 // handling for curves. Add start point
1758 const Point
aStartPoint(mpImplPolygon
->mpPointAry
[0]);
1759 sal_uInt8
nPointFlag(mpImplPolygon
->mpFlagAry
[0]);
1760 aRetval
.append(basegfx::B2DPoint(aStartPoint
.X(), aStartPoint
.Y()));
1761 Point aControlA
, aControlB
;
1763 for(sal_uInt16
a(1); a
< nCount
;)
1765 bool bControlA(false);
1766 bool bControlB(false);
1768 if(POLY_CONTROL
== mpImplPolygon
->mpFlagAry
[a
])
1770 aControlA
= mpImplPolygon
->mpPointAry
[a
++];
1774 if(a
< nCount
&& POLY_CONTROL
== mpImplPolygon
->mpFlagAry
[a
])
1776 aControlB
= mpImplPolygon
->mpPointAry
[a
++];
1780 // assert invalid polygons
1781 OSL_ENSURE(bControlA
== bControlB
, "Polygon::getB2DPolygon: Invalid source polygon (!)");
1786 const Point
aEndPoint(mpImplPolygon
->mpPointAry
[a
]);
1791 aRetval
.appendBezierSegment(
1792 basegfx::B2DPoint(aControlA
.X(), aControlA
.Y()),
1793 basegfx::B2DPoint(aControlB
.X(), aControlB
.Y()),
1794 basegfx::B2DPoint(aEndPoint
.X(), aEndPoint
.Y()));
1796 impCorrectContinuity(aRetval
, aRetval
.count() - 2, nPointFlag
);
1800 // no bezier edge, add end point
1801 aRetval
.append(basegfx::B2DPoint(aEndPoint
.X(), aEndPoint
.Y()));
1804 nPointFlag
= mpImplPolygon
->mpFlagAry
[a
++];
1808 // if exist, remove double first/last points, set closed and correct control points
1809 basegfx::tools::checkClosed(aRetval
);
1811 if(aRetval
.isClosed())
1813 // closeWithGeometryChange did really close, so last point(s) were removed.
1814 // Correct the continuity in the changed point
1815 impCorrectContinuity(aRetval
, 0, mpImplPolygon
->mpFlagAry
[0]);
1820 // extra handling for non-curves (most-used case) for speedup
1821 for(sal_uInt16
a(0); a
< nCount
; a
++)
1823 // get point and add
1824 const Point
aPoint(mpImplPolygon
->mpPointAry
[a
]);
1825 aRetval
.append(basegfx::B2DPoint(aPoint
.X(), aPoint
.Y()));
1829 basegfx::tools::checkClosed(aRetval
);
1836 // constructor to convert from basegfx::B2DPolygon
1837 // #i76891# Needed to change from adding all control points (even for unused
1838 // edges) and creating a fixed-size Polygon in the first run to creating the
1839 // minimal Polygon. This requires a temporary Point- and Flag-Array for curves
1840 // and a memcopy at ImplPolygon creation, but contains no zero-controlpoints
1841 // for straight edges.
1842 Polygon::Polygon(const basegfx::B2DPolygon
& rPolygon
)
1845 DBG_CTOR( Polygon
, NULL
);
1847 const bool bCurve(rPolygon
.areControlPointsUsed());
1848 const bool bClosed(rPolygon
.isClosed());
1849 sal_uInt32
nB2DLocalCount(rPolygon
.count());
1853 // #127979# Reduce source point count hard to the limit of the tools Polygon
1854 if(nB2DLocalCount
> ((0x0000ffff / 3L) - 1L))
1856 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)");
1857 nB2DLocalCount
= ((0x0000ffff / 3L) - 1L);
1860 // calculate target point count
1861 const sal_uInt32
nLoopCount(bClosed
? nB2DLocalCount
: (nB2DLocalCount
? nB2DLocalCount
- 1L : 0L ));
1865 // calculate maximum array size and allocate; prepare insert index
1866 const sal_uInt32
nMaxTargetCount((nLoopCount
* 3) + 1);
1867 mpImplPolygon
= new ImplPolygon(static_cast< sal_uInt16
>(nMaxTargetCount
), true);
1869 // prepare insert index and current point
1870 sal_uInt32
nArrayInsert(0);
1871 basegfx::B2DCubicBezier aBezier
;
1872 aBezier
.setStartPoint(rPolygon
.getB2DPoint(0));
1874 for(sal_uInt32
a(0L); a
< nLoopCount
; a
++)
1876 // add current point (always) and remember StartPointIndex for evtl. later corrections
1877 const Point
aStartPoint(FRound(aBezier
.getStartPoint().getX()), FRound(aBezier
.getStartPoint().getY()));
1878 const sal_uInt32
nStartPointIndex(nArrayInsert
);
1879 mpImplPolygon
->mpPointAry
[nStartPointIndex
] = aStartPoint
;
1880 mpImplPolygon
->mpFlagAry
[nStartPointIndex
] = (sal_uInt8
)POLY_NORMAL
;
1883 // prepare next segment
1884 const sal_uInt32
nNextIndex((a
+ 1) % nB2DLocalCount
);
1885 aBezier
.setEndPoint(rPolygon
.getB2DPoint(nNextIndex
));
1886 aBezier
.setControlPointA(rPolygon
.getNextControlPoint(a
));
1887 aBezier
.setControlPointB(rPolygon
.getPrevControlPoint(nNextIndex
));
1889 if(aBezier
.isBezier())
1891 // if one is used, add always two control points due to the old schema
1892 mpImplPolygon
->mpPointAry
[nArrayInsert
] = Point(FRound(aBezier
.getControlPointA().getX()), FRound(aBezier
.getControlPointA().getY()));
1893 mpImplPolygon
->mpFlagAry
[nArrayInsert
] = (sal_uInt8
)POLY_CONTROL
;
1896 mpImplPolygon
->mpPointAry
[nArrayInsert
] = Point(FRound(aBezier
.getControlPointB().getX()), FRound(aBezier
.getControlPointB().getY()));
1897 mpImplPolygon
->mpFlagAry
[nArrayInsert
] = (sal_uInt8
)POLY_CONTROL
;
1901 // test continuity with previous control point to set flag value
1902 if(aBezier
.getControlPointA() != aBezier
.getStartPoint() && (bClosed
|| a
))
1904 const basegfx::B2VectorContinuity
eCont(rPolygon
.getContinuityInPoint(a
));
1906 if(basegfx::CONTINUITY_C1
== eCont
)
1908 mpImplPolygon
->mpFlagAry
[nStartPointIndex
] = (sal_uInt8
)POLY_SMOOTH
;
1910 else if(basegfx::CONTINUITY_C2
== eCont
)
1912 mpImplPolygon
->mpFlagAry
[nStartPointIndex
] = (sal_uInt8
)POLY_SYMMTR
;
1916 // prepare next polygon step
1917 aBezier
.setStartPoint(aBezier
.getEndPoint());
1922 // add first point again as closing point due to old definition
1923 mpImplPolygon
->mpPointAry
[nArrayInsert
] = mpImplPolygon
->mpPointAry
[0];
1924 mpImplPolygon
->mpFlagAry
[nArrayInsert
] = (sal_uInt8
)POLY_NORMAL
;
1929 // add last point as closing point
1930 const basegfx::B2DPoint
aClosingPoint(rPolygon
.getB2DPoint(nB2DLocalCount
- 1L));
1931 const Point
aEnd(FRound(aClosingPoint
.getX()), FRound(aClosingPoint
.getY()));
1932 mpImplPolygon
->mpPointAry
[nArrayInsert
] = aEnd
;
1933 mpImplPolygon
->mpFlagAry
[nArrayInsert
] = (sal_uInt8
)POLY_NORMAL
;
1937 DBG_ASSERT(nArrayInsert
<= nMaxTargetCount
, "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)");
1939 if(nArrayInsert
!= nMaxTargetCount
)
1941 mpImplPolygon
->ImplSetSize(static_cast< sal_uInt16
>(nArrayInsert
), true);
1947 // #127979# Reduce source point count hard to the limit of the tools Polygon
1948 if(nB2DLocalCount
> (0x0000ffff - 1L))
1950 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)");
1951 nB2DLocalCount
= (0x0000ffff - 1L);
1956 // point list creation
1957 const sal_uInt32
nTargetCount(nB2DLocalCount
+ (bClosed
? 1L : 0L));
1958 mpImplPolygon
= new ImplPolygon( static_cast< sal_uInt16
>(nTargetCount
) );
1959 sal_uInt16
nIndex(0);
1961 for(sal_uInt32
a(0L); a
< nB2DLocalCount
; a
++)
1963 basegfx::B2DPoint
aB2DPoint(rPolygon
.getB2DPoint(a
));
1964 Point
aPoint(FRound(aB2DPoint
.getX()), FRound(aB2DPoint
.getY()));
1965 mpImplPolygon
->mpPointAry
[nIndex
++] = aPoint
;
1970 // add first point as closing point
1971 mpImplPolygon
->mpPointAry
[nIndex
] = mpImplPolygon
->mpPointAry
[0];
1978 // no content yet, create empty polygon
1979 mpImplPolygon
= (ImplPolygon
*)(&aStaticImplPolygon
);
1983 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */