diff options
Diffstat (limited to 'tools/source/generic/poly.cxx')
-rw-r--r-- | tools/source/generic/poly.cxx | 2375 |
1 files changed, 2375 insertions, 0 deletions
diff --git a/tools/source/generic/poly.cxx b/tools/source/generic/poly.cxx new file mode 100644 index 000000000000..2290cfdbe7c2 --- /dev/null +++ b/tools/source/generic/poly.cxx @@ -0,0 +1,2375 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _SV_POLY_CXX +#include <osl/endian.h> +#include <tools/bigint.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <poly.h> +#include <tools/line.hxx> +#ifndef _VECTOR2D_H +#include <tools/vector2d.hxx> +#endif +#ifndef _POLY_HXX +#include <tools/poly.hxx> +#endif +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/curve/b2dcubicbezier.hxx> + +#include <vector> +#include <iterator> +#include <algorithm> +#include <cstring> +#include <limits.h> +#include <cmath> + + +// ======================================================================= + +DBG_NAME( Polygon ) + +// ----------------------------------------------------------------------- + +#define EDGE_LEFT 1 +#define EDGE_TOP 2 +#define EDGE_RIGHT 4 +#define EDGE_BOTTOM 8 +#define EDGE_HORZ (EDGE_RIGHT | EDGE_LEFT) +#define EDGE_VERT (EDGE_TOP | EDGE_BOTTOM) +#define SMALL_DVALUE 0.0000001 +#define FSQRT2 1.4142135623730950488016887242097 + +// ----------------------------------------------------------------------- + +static ImplPolygonData aStaticImplPolygon = +{ + NULL, NULL, 0, 0 +}; + +// ======================================================================= + +ImplPolygon::ImplPolygon( USHORT nInitSize, BOOL bFlags ) +{ + if ( nInitSize ) + { + mpPointAry = (Point*)new char[(ULONG)nInitSize*sizeof(Point)]; + memset( mpPointAry, 0, (ULONG)nInitSize*sizeof(Point) ); + } + else + mpPointAry = NULL; + + if( bFlags ) + { + mpFlagAry = new BYTE[ nInitSize ]; + memset( mpPointAry, 0, nInitSize ); + } + else + mpFlagAry = NULL; + + mnRefCount = 1; + mnPoints = nInitSize; +} + +// ----------------------------------------------------------------------- + +ImplPolygon::ImplPolygon( const ImplPolygon& rImpPoly ) +{ + if ( rImpPoly.mnPoints ) + { + mpPointAry = (Point*)new char[(ULONG)rImpPoly.mnPoints*sizeof(Point)]; + memcpy( mpPointAry, rImpPoly.mpPointAry, (ULONG)rImpPoly.mnPoints*sizeof(Point) ); + + if( rImpPoly.mpFlagAry ) + { + mpFlagAry = new BYTE[ rImpPoly.mnPoints ]; + memcpy( mpFlagAry, rImpPoly.mpFlagAry, rImpPoly.mnPoints ); + } + else + mpFlagAry = NULL; + } + else + { + mpPointAry = NULL; + mpFlagAry = NULL; + } + + mnRefCount = 1; + mnPoints = rImpPoly.mnPoints; +} + +// ----------------------------------------------------------------------- + +ImplPolygon::ImplPolygon( USHORT nInitSize, const Point* pInitAry, const BYTE* pInitFlags ) +{ + if ( nInitSize ) + { + mpPointAry = (Point*)new char[(ULONG)nInitSize*sizeof(Point)]; + memcpy( mpPointAry, pInitAry, (ULONG)nInitSize*sizeof( Point ) ); + + if( pInitFlags ) + { + mpFlagAry = new BYTE[ nInitSize ]; + memcpy( mpFlagAry, pInitFlags, nInitSize ); + } + else + mpFlagAry = NULL; + } + else + { + mpPointAry = NULL; + mpFlagAry = NULL; + } + + mnRefCount = 1; + mnPoints = nInitSize; +} + +// ----------------------------------------------------------------------- + +ImplPolygon::~ImplPolygon() +{ + if ( mpPointAry ) + { + delete[] (char*) mpPointAry; + } + + if( mpFlagAry ) + delete[] mpFlagAry; +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplSetSize( USHORT nNewSize, BOOL bResize ) +{ + if( mnPoints == nNewSize ) + return; + + Point* pNewAry; + + if ( nNewSize ) + { + pNewAry = (Point*)new char[(ULONG)nNewSize*sizeof(Point)]; + + if ( bResize ) + { + // Alte Punkte kopieren + if ( mnPoints < nNewSize ) + { + // Neue Punkte mit 0 initialisieren + memset( pNewAry+mnPoints, 0, (ULONG)(nNewSize-mnPoints)*sizeof(Point) ); + if ( mpPointAry ) + memcpy( pNewAry, mpPointAry, mnPoints*sizeof(Point) ); + } + else + { + if ( mpPointAry ) + memcpy( pNewAry, mpPointAry, (ULONG)nNewSize*sizeof(Point) ); + } + } + } + else + pNewAry = NULL; + + if ( mpPointAry ) + delete[] (char*) mpPointAry; + + // ggf. FlagArray beruecksichtigen + if( mpFlagAry ) + { + BYTE* pNewFlagAry; + + if( nNewSize ) + { + pNewFlagAry = new BYTE[ nNewSize ]; + + if( bResize ) + { + // Alte Flags kopieren + if ( mnPoints < nNewSize ) + { + // Neue Punkte mit 0 initialisieren + memset( pNewFlagAry+mnPoints, 0, nNewSize-mnPoints ); + memcpy( pNewFlagAry, mpFlagAry, mnPoints ); + } + else + memcpy( pNewFlagAry, mpFlagAry, nNewSize ); + } + } + else + pNewFlagAry = NULL; + + delete[] mpFlagAry; + mpFlagAry = pNewFlagAry; + } + + mpPointAry = pNewAry; + mnPoints = nNewSize; +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplSplit( USHORT nPos, USHORT nSpace, ImplPolygon* pInitPoly ) +{ + const ULONG nSpaceSize = nSpace * sizeof( Point ); + const USHORT nNewSize = mnPoints + nSpace; + + if( nPos >= mnPoints ) + { + // Hinten anhaengen + nPos = mnPoints; + ImplSetSize( nNewSize, TRUE ); + + if( pInitPoly ) + { + memcpy( mpPointAry + nPos, pInitPoly->mpPointAry, nSpaceSize ); + + if( pInitPoly->mpFlagAry ) + memcpy( mpFlagAry + nPos, pInitPoly->mpFlagAry, nSpace ); + } + } + else + { + // PointArray ist in diesem Zweig immer vorhanden + const USHORT nSecPos = nPos + nSpace; + const USHORT nRest = mnPoints - nPos; + + Point* pNewAry = (Point*) new char[ (ULONG) nNewSize * sizeof( Point ) ]; + + memcpy( pNewAry, mpPointAry, nPos * sizeof( Point ) ); + + if( pInitPoly ) + memcpy( pNewAry + nPos, pInitPoly->mpPointAry, nSpaceSize ); + else + memset( pNewAry + nPos, 0, nSpaceSize ); + + memcpy( pNewAry + nSecPos, mpPointAry + nPos, nRest * sizeof( Point ) ); + delete[] (char*) mpPointAry; + + // ggf. FlagArray beruecksichtigen + if( mpFlagAry ) + { + BYTE* pNewFlagAry = new BYTE[ nNewSize ]; + + memcpy( pNewFlagAry, mpFlagAry, nPos ); + + if( pInitPoly && pInitPoly->mpFlagAry ) + memcpy( pNewFlagAry + nPos, pInitPoly->mpFlagAry, nSpace ); + else + memset( pNewFlagAry + nPos, 0, nSpace ); + + memcpy( pNewFlagAry + nSecPos, mpFlagAry + nPos, nRest ); + delete[] mpFlagAry; + mpFlagAry = pNewFlagAry; + } + + mpPointAry = pNewAry; + mnPoints = nNewSize; + } +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplRemove( USHORT nPos, USHORT nCount ) +{ + const USHORT nRemoveCount = Min( (USHORT) ( mnPoints - nPos ), (USHORT) nCount ); + + if( nRemoveCount ) + { + const USHORT nNewSize = mnPoints - nRemoveCount; + const USHORT nSecPos = nPos + nRemoveCount; + const USHORT nRest = mnPoints - nSecPos; + + Point* pNewAry = (Point*) new char[ (ULONG) nNewSize * sizeof( Point ) ]; + + memcpy( pNewAry, mpPointAry, nPos * sizeof( Point ) ); + memcpy( pNewAry + nPos, mpPointAry + nSecPos, nRest * sizeof( Point ) ); + + delete[] (char*) mpPointAry; + + // ggf. FlagArray beruecksichtigen + if( mpFlagAry ) + { + BYTE* pNewFlagAry = new BYTE[ nNewSize ]; + + memcpy( pNewFlagAry, mpFlagAry, nPos ); + memcpy( pNewFlagAry + nPos, mpFlagAry + nSecPos, nRest ); + delete[] mpFlagAry; + mpFlagAry = pNewFlagAry; + } + + mpPointAry = pNewAry; + mnPoints = nNewSize; + } +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplCreateFlagArray() +{ + if( !mpFlagAry ) + { + mpFlagAry = new BYTE[ mnPoints ]; + memset( mpFlagAry, 0, mnPoints ); + } +} + +// ======================================================================= + +inline void Polygon::ImplMakeUnique() +{ + // Falls noch andere Referenzen bestehen, dann kopieren + if ( mpImplPolygon->mnRefCount != 1 ) + { + if ( mpImplPolygon->mnRefCount ) + mpImplPolygon->mnRefCount--; + mpImplPolygon = new ImplPolygon( *mpImplPolygon ); + } +} + +// ----------------------------------------------------------------------- + +inline double ImplGetAngle( const Point& rCenter, const Point& rPt ) +{ + const long nDX = rPt.X() - rCenter.X(); + return( atan2( -rPt.Y() + rCenter.Y(), ( ( nDX == 0L ) ? 0.000000001 : nDX ) ) ); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon() +{ + DBG_CTOR( Polygon, NULL ); + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( USHORT nSize ) +{ + DBG_CTOR( Polygon, NULL ); + + if ( nSize ) + mpImplPolygon = new ImplPolygon( nSize ); + else + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( USHORT nPoints, const Point* pPtAry, const BYTE* pFlagAry ) +{ + DBG_CTOR( Polygon, NULL ); + + if( nPoints ) + mpImplPolygon = new ImplPolygon( nPoints, pPtAry, pFlagAry ); + else + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Polygon& rPoly ) +{ + DBG_CTOR( Polygon, NULL ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERT( rPoly.mpImplPolygon->mnRefCount < 0xFFFFFFFE, "Polygon: RefCount overflow" ); + + mpImplPolygon = rPoly.mpImplPolygon; + if ( mpImplPolygon->mnRefCount ) + mpImplPolygon->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Rectangle& rRect ) +{ + DBG_CTOR( Polygon, NULL ); + + if ( rRect.IsEmpty() ) + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); + else + { + mpImplPolygon = new ImplPolygon( 5 ); + mpImplPolygon->mpPointAry[0] = rRect.TopLeft(); + mpImplPolygon->mpPointAry[1] = rRect.TopRight(); + mpImplPolygon->mpPointAry[2] = rRect.BottomRight(); + mpImplPolygon->mpPointAry[3] = rRect.BottomLeft(); + mpImplPolygon->mpPointAry[4] = rRect.TopLeft(); + } +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Rectangle& rRect, ULONG nHorzRound, ULONG nVertRound ) +{ + DBG_CTOR( Polygon, NULL ); + + if ( rRect.IsEmpty() ) + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); + else + { + Rectangle aRect( rRect ); + aRect.Justify(); // SJ: i9140 + + nHorzRound = Min( nHorzRound, (ULONG) labs( aRect.GetWidth() >> 1 ) ); + nVertRound = Min( nVertRound, (ULONG) labs( aRect.GetHeight() >> 1 ) ); + + if( !nHorzRound && !nVertRound ) + { + mpImplPolygon = new ImplPolygon( 5 ); + mpImplPolygon->mpPointAry[0] = aRect.TopLeft(); + mpImplPolygon->mpPointAry[1] = aRect.TopRight(); + mpImplPolygon->mpPointAry[2] = aRect.BottomRight(); + mpImplPolygon->mpPointAry[3] = aRect.BottomLeft(); + mpImplPolygon->mpPointAry[4] = aRect.TopLeft(); + } + else + { + const Point aTL( aRect.Left() + nHorzRound, aRect.Top() + nVertRound ); + const Point aTR( aRect.Right() - nHorzRound, aRect.Top() + nVertRound ); + const Point aBR( aRect.Right() - nHorzRound, aRect.Bottom() - nVertRound ); + const Point aBL( aRect.Left() + nHorzRound, aRect.Bottom() - nVertRound ); + Polygon* pEllipsePoly = new Polygon( Point(), nHorzRound, nVertRound ); + USHORT i, nEnd, nSize4 = pEllipsePoly->GetSize() >> 2; + + mpImplPolygon = new ImplPolygon( pEllipsePoly->GetSize() + 1 ); + + const Point* pSrcAry = pEllipsePoly->GetConstPointAry(); + Point* pDstAry = mpImplPolygon->mpPointAry; + + for( i = 0, nEnd = nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aTR; + + for( nEnd = nEnd + nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aTL; + + for( nEnd = nEnd + nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aBL; + + for( nEnd = nEnd + nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aBR; + + pDstAry[ nEnd ] = pDstAry[ 0 ]; + delete pEllipsePoly; + } + } +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Point& rCenter, long nRadX, long nRadY, USHORT nPoints ) +{ + DBG_CTOR( Polygon, NULL ); + + if( nRadX && nRadY ) + { + // Default berechnen (abhaengig von Groesse) + if( !nPoints ) + { + nPoints = (USHORT) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - + sqrt( (double) labs( nRadX * nRadY ) ) ) ); + + nPoints = (USHORT) MinMax( nPoints, 32, 256 ); + + if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 ) + nPoints >>= 1; + } + + // Anzahl der Punkte auf durch 4 teilbare Zahl aufrunden + mpImplPolygon = new ImplPolygon( nPoints = (nPoints + 3) & ~3 ); + + Point* pPt; + USHORT i; + USHORT nPoints2 = nPoints >> 1; + USHORT nPoints4 = nPoints >> 2; + double nAngle; + double nAngleStep = F_PI2 / ( nPoints4 - 1 ); + + for( i=0, nAngle = 0.0; i < nPoints4; i++, nAngle += nAngleStep ) + { + long nX = FRound( nRadX * cos( nAngle ) ); + long nY = FRound( -nRadY * sin( nAngle ) ); + + pPt = &(mpImplPolygon->mpPointAry[i]); + pPt->X() = nX + rCenter.X(); + pPt->Y() = nY + rCenter.Y(); + pPt = &(mpImplPolygon->mpPointAry[nPoints2-i-1]); + pPt->X() = -nX + rCenter.X(); + pPt->Y() = nY + rCenter.Y(); + pPt = &(mpImplPolygon->mpPointAry[i+nPoints2]); + pPt->X() = -nX + rCenter.X(); + pPt->Y() = -nY + rCenter.Y(); + pPt = &(mpImplPolygon->mpPointAry[nPoints-i-1]); + pPt->X() = nX + rCenter.X(); + pPt->Y() = -nY + rCenter.Y(); + } + } + else + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Rectangle& rBound, + const Point& rStart, const Point& rEnd, PolyStyle eStyle ) +{ + DBG_CTOR( Polygon, NULL ); + + const long nWidth = rBound.GetWidth(); + const long nHeight = rBound.GetHeight(); + + if( ( nWidth > 1 ) && ( nHeight > 1 ) ) + { + const Point aCenter( rBound.Center() ); + const long nRadX = aCenter.X() - rBound.Left(); + const long nRadY = aCenter.Y() - rBound.Top(); + USHORT nPoints; + + nPoints = (USHORT) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - + sqrt( (double) labs( nRadX * nRadY ) ) ) ); + + nPoints = (USHORT) MinMax( nPoints, 32, 256 ); + + if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 ) + nPoints >>= 1; + + // Winkel berechnen + const double fRadX = nRadX; + const double fRadY = nRadY; + const double fCenterX = aCenter.X(); + const double fCenterY = aCenter.Y(); + double fStart = ImplGetAngle( aCenter, rStart ); + double fEnd = ImplGetAngle( aCenter, rEnd ); + double fDiff = fEnd - fStart; + double fStep; + USHORT nStart; + USHORT nEnd; + + if( fDiff < 0. ) + fDiff += F_2PI; + + // Punktanzahl proportional verkleinern ( fDiff / (2PI) ); + // ist eingentlich nur fuer einen Kreis richtig; wir + // machen es hier aber trotzdem + nPoints = Max( (USHORT) ( ( fDiff * 0.1591549 ) * nPoints ), (USHORT) 16 ); + fStep = fDiff / ( nPoints - 1 ); + + if( POLY_PIE == eStyle ) + { + const Point aCenter2( FRound( fCenterX ), FRound( fCenterY ) ); + + nStart = 1; + nEnd = nPoints + 1; + mpImplPolygon = new ImplPolygon( nPoints + 2 ); + mpImplPolygon->mpPointAry[ 0 ] = aCenter2; + mpImplPolygon->mpPointAry[ nEnd ] = aCenter2; + } + else + { + mpImplPolygon = new ImplPolygon( ( POLY_CHORD == eStyle ) ? ( nPoints + 1 ) : nPoints ); + nStart = 0; + nEnd = nPoints; + } + + for(; nStart < nEnd; nStart++, fStart += fStep ) + { + Point& rPt = mpImplPolygon->mpPointAry[ nStart ]; + + rPt.X() = FRound( fCenterX + fRadX * cos( fStart ) ); + rPt.Y() = FRound( fCenterY - fRadY * sin( fStart ) ); + } + + if( POLY_CHORD == eStyle ) + mpImplPolygon->mpPointAry[ nPoints ] = mpImplPolygon->mpPointAry[ 0 ]; + } + else + mpImplPolygon = (ImplPolygon*) &aStaticImplPolygon; +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Point& rBezPt1, const Point& rCtrlPt1, + const Point& rBezPt2, const Point& rCtrlPt2, + USHORT nPoints ) +{ + DBG_CTOR( Polygon, NULL ); + + nPoints = ( 0 == nPoints ) ? 25 : ( ( nPoints < 2 ) ? 2 : nPoints ); + + const double fInc = 1.0 / ( nPoints - 1 ); + double fK_1 = 0.0, fK1_1 = 1.0; + double fK_2, fK_3, fK1_2, fK1_3, fK12, fK21; + const double fX0 = rBezPt1.X(); + const double fY0 = rBezPt1.Y(); + const double fX1 = 3.0 * rCtrlPt1.X(); + const double fY1 = 3.0 * rCtrlPt1.Y(); + const double fX2 = 3.0 * rCtrlPt2.X();; + const double fY2 = 3.0 * rCtrlPt2.Y();; + const double fX3 = rBezPt2.X(); + const double fY3 = rBezPt2.Y(); + + mpImplPolygon = new ImplPolygon( nPoints ); + + for( USHORT i = 0; i < nPoints; i++, fK_1 += fInc, fK1_1 -= fInc ) + { + Point& rPt = mpImplPolygon->mpPointAry[ i ]; + + fK_2 = fK_1, fK_3 = ( fK_2 *= fK_1 ), fK_3 *= fK_1; + fK1_2 = fK1_1, fK1_3 = ( fK1_2 *= fK1_1 ), fK1_3 *= fK1_1; + fK12 = fK_1 * fK1_2, fK21 = fK_2 * fK1_1; + + rPt.X() = FRound( fK1_3 * fX0 + fK12 * fX1 + fK21 * fX2 + fK_3 * fX3 ); + rPt.Y() = FRound( fK1_3 * fY0 + fK12 * fY1 + fK21 * fY2 + fK_3 * fY3 ); + } +} + +// ----------------------------------------------------------------------- + +Polygon::~Polygon() +{ + DBG_DTOR( Polygon, NULL ); + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } +} + +// ----------------------------------------------------------------------- + +Point* Polygon::ImplGetPointAry() +{ + DBG_CHKTHIS( Polygon, NULL ); + + ImplMakeUnique(); + return (Point*)mpImplPolygon->mpPointAry; +} + +// ----------------------------------------------------------------------- + +BYTE* Polygon::ImplGetFlagAry() +{ + DBG_CHKTHIS( Polygon, NULL ); + + ImplMakeUnique(); + mpImplPolygon->ImplCreateFlagArray(); + return mpImplPolygon->mpFlagAry; +} + +// ----------------------------------------------------------------------- + +const Point* Polygon::GetConstPointAry() const +{ + DBG_CHKTHIS( Polygon, NULL ); + return (Point*)mpImplPolygon->mpPointAry; +} + +// ----------------------------------------------------------------------- + +const BYTE* Polygon::GetConstFlagAry() const +{ + DBG_CHKTHIS( Polygon, NULL ); + return mpImplPolygon->mpFlagAry; +} + +// ----------------------------------------------------------------------- + +void Polygon::SetPoint( const Point& rPt, USHORT nPos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::SetPoint(): nPos >= nPoints" ); + + ImplMakeUnique(); + mpImplPolygon->mpPointAry[nPos] = rPt; +} + +// ----------------------------------------------------------------------- + +void Polygon::SetFlags( USHORT nPos, PolyFlags eFlags ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::SetFlags(): nPos >= nPoints" ); + + // we do only want to create the flag array if there + // is at least one flag different to POLY_NORMAL + if ( mpImplPolygon || ( eFlags != POLY_NORMAL ) ) + { + ImplMakeUnique(); + mpImplPolygon->ImplCreateFlagArray(); + mpImplPolygon->mpFlagAry[ nPos ] = (BYTE) eFlags; + } +} + +// ----------------------------------------------------------------------- + +const Point& Polygon::GetPoint( USHORT nPos ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetPoint(): nPos >= nPoints" ); + + return mpImplPolygon->mpPointAry[nPos]; +} + +// ----------------------------------------------------------------------- + +PolyFlags Polygon::GetFlags( USHORT nPos ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetFlags(): nPos >= nPoints" ); + return( mpImplPolygon->mpFlagAry ? + (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : + POLY_NORMAL ); +} + +// ----------------------------------------------------------------------- + +sal_Bool Polygon::HasFlags() const +{ + return mpImplPolygon->mpFlagAry != NULL; +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsControl(USHORT nPos) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetFlags(): nPos >= nPoints" ); + PolyFlags eFlags = mpImplPolygon->mpFlagAry ? + (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : POLY_NORMAL; + + return( POLY_CONTROL == eFlags ); +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsSmooth(USHORT nPos) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetFlags(): nPos >= nPoints" ); + PolyFlags eFlags = mpImplPolygon->mpFlagAry ? + (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : POLY_NORMAL; + + return( ( POLY_SMOOTH == eFlags ) || ( POLY_SYMMTR == eFlags ) ); +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsRect() const +{ + BOOL bIsRect = FALSE; + if ( mpImplPolygon->mpFlagAry == NULL ) + { + if ( ( ( mpImplPolygon->mnPoints == 5 ) && ( mpImplPolygon->mpPointAry[ 0 ] == mpImplPolygon->mpPointAry[ 4 ] ) ) || + ( mpImplPolygon->mnPoints == 4 ) ) + { + if ( ( mpImplPolygon->mpPointAry[ 0 ].X() == mpImplPolygon->mpPointAry[ 3 ].X() ) && + ( mpImplPolygon->mpPointAry[ 0 ].Y() == mpImplPolygon->mpPointAry[ 1 ].Y() ) && + ( mpImplPolygon->mpPointAry[ 1 ].X() == mpImplPolygon->mpPointAry[ 2 ].X() ) && + ( mpImplPolygon->mpPointAry[ 2 ].Y() == mpImplPolygon->mpPointAry[ 3 ].Y() ) ) + bIsRect = TRUE; + } + } + return bIsRect; +} + +// ----------------------------------------------------------------------- + +void Polygon::SetSize( USHORT nNewSize ) +{ + DBG_CHKTHIS( Polygon, NULL ); + + if( nNewSize != mpImplPolygon->mnPoints ) + { + ImplMakeUnique(); + mpImplPolygon->ImplSetSize( nNewSize ); + } +} + +// ----------------------------------------------------------------------- + +USHORT Polygon::GetSize() const +{ + DBG_CHKTHIS( Polygon, NULL ); + + return mpImplPolygon->mnPoints; +} + +// ----------------------------------------------------------------------- + +void Polygon::Clear() +{ + DBG_CHKTHIS( Polygon, NULL ); + + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } + + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +double Polygon::CalcDistance( USHORT nP1, USHORT nP2 ) +{ + DBG_ASSERT( nP1 < mpImplPolygon->mnPoints, + "Polygon::CalcDistance(): nPos1 >= nPoints" ); + DBG_ASSERT( nP2 < mpImplPolygon->mnPoints, + "Polygon::CalcDistance(): nPos2 >= nPoints" ); + + const Point& rP1 = mpImplPolygon->mpPointAry[ nP1 ]; + const Point& rP2 = mpImplPolygon->mpPointAry[ nP2 ]; + const double fDx = rP2.X() - rP1.X(); + const double fDy = rP2.Y() - rP1.Y(); + + return sqrt( fDx * fDx + fDy * fDy ); +} + +// ----------------------------------------------------------------------- + +void Polygon::Optimize( ULONG nOptimizeFlags, const PolyOptimizeData* pData ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "Optimizing could fail with beziers!" ); + + USHORT nSize = mpImplPolygon->mnPoints; + + if( nOptimizeFlags && nSize ) + { + if( nOptimizeFlags & POLY_OPTIMIZE_EDGES ) + { + const Rectangle aBound( GetBoundRect() ); + const double fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5; + const USHORT nPercent = pData ? pData->GetPercentValue() : 50; + + Optimize( POLY_OPTIMIZE_NO_SAME ); + ImplReduceEdges( *this, fArea, nPercent ); + } + else if( nOptimizeFlags & ( POLY_OPTIMIZE_REDUCE | POLY_OPTIMIZE_NO_SAME ) ) + { + Polygon aNewPoly; + const Point& rFirst = mpImplPolygon->mpPointAry[ 0 ]; + ULONG nReduce; + + if( nOptimizeFlags & ( POLY_OPTIMIZE_REDUCE ) ) + nReduce = pData ? pData->GetAbsValue() : 4UL; + else + nReduce = 0UL; + + while( nSize && ( mpImplPolygon->mpPointAry[ nSize - 1 ] == rFirst ) ) + nSize--; + + if( nSize > 1 ) + { + USHORT nLast = 0, nNewCount = 1; + + aNewPoly.SetSize( nSize ); + aNewPoly[ 0 ] = rFirst; + + for( USHORT i = 1; i < nSize; i++ ) + { + if( ( mpImplPolygon->mpPointAry[ i ] != mpImplPolygon->mpPointAry[ nLast ] ) && + ( !nReduce || ( nReduce < (ULONG) FRound( CalcDistance( nLast, i ) ) ) ) ) + { + aNewPoly[ nNewCount++ ] = mpImplPolygon->mpPointAry[ nLast = i ]; + } + } + + if( nNewCount == 1 ) + aNewPoly.Clear(); + else + aNewPoly.SetSize( nNewCount ); + } + + *this = aNewPoly; + } + + nSize = mpImplPolygon->mnPoints; + + if( nSize > 1 ) + { + if( ( nOptimizeFlags & POLY_OPTIMIZE_CLOSE ) && + ( mpImplPolygon->mpPointAry[ 0 ] != mpImplPolygon->mpPointAry[ nSize - 1 ] ) ) + { + SetSize( mpImplPolygon->mnPoints + 1 ); + mpImplPolygon->mpPointAry[ mpImplPolygon->mnPoints - 1 ] = mpImplPolygon->mpPointAry[ 0 ]; + } + else if( ( nOptimizeFlags & POLY_OPTIMIZE_OPEN ) && + ( mpImplPolygon->mpPointAry[ 0 ] == mpImplPolygon->mpPointAry[ nSize - 1 ] ) ) + { + const Point& rFirst = mpImplPolygon->mpPointAry[ 0 ]; + + while( nSize && ( mpImplPolygon->mpPointAry[ nSize - 1 ] == rFirst ) ) + nSize--; + + SetSize( nSize ); + } + } + } +} + +// ======================================================================= + +/* Recursively subdivide cubic bezier curve via deCasteljau. + + @param rPointIter + Output iterator, where the subdivided polylines are written to. + + @param d + Squared difference of curve to a straight line + + @param P* + Exactly four points, interpreted as support and control points of + a cubic bezier curve. Must be in device coordinates, since stop + criterion is based on the following assumption: the device has a + finite resolution, it is thus sufficient to stop subdivision if the + curve does not deviate more than one pixel from a straight line. + +*/ +static void ImplAdaptiveSubdivide( ::std::back_insert_iterator< ::std::vector< Point > >& rPointIter, + const double old_d2, + int recursionDepth, + const double d2, + const double P1x, const double P1y, + const double P2x, const double P2y, + const double P3x, const double P3y, + const double P4x, const double P4y ) +{ + // Hard limit on recursion depth, empiric number. + enum {maxRecursionDepth=128}; + + // Perform bezier flatness test (lecture notes from R. Schaback, + // Mathematics of Computer-Aided Design, Uni Goettingen, 2000) + // + // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)|| + // 0<=j<=n + // + // What is calculated here is an upper bound to the distance from + // a line through b_0 and b_3 (P1 and P4 in our notation) and the + // curve. We can drop 0 and n from the running indices, since the + // argument of max becomes zero for those cases. + const double fJ1x( P2x - P1x - 1.0/3.0*(P4x - P1x) ); + const double fJ1y( P2y - P1y - 1.0/3.0*(P4y - P1y) ); + const double fJ2x( P3x - P1x - 2.0/3.0*(P4x - P1x) ); + const double fJ2y( P3y - P1y - 2.0/3.0*(P4y - P1y) ); + const double distance2( ::std::max( fJ1x*fJ1x + fJ1y*fJ1y, + fJ2x*fJ2x + fJ2y*fJ2y) ); + + // stop if error measure does not improve anymore. This is a + // safety guard against floating point inaccuracies. + // stop at recursion level 128. This is a safety guard against + // floating point inaccuracies. + // stop if distance from line is guaranteed to be bounded by d + if( old_d2 > d2 && + recursionDepth < maxRecursionDepth && + distance2 >= d2 ) + { + // deCasteljau bezier arc, split at t=0.5 + // Foley/vanDam, p. 508 + const double L1x( P1x ), L1y( P1y ); + const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 ); + const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 ); + const double L3x( (L2x + Hx)*0.5 ), L3y( (L2y + Hy)*0.5 ); + const double R4x( P4x ), R4y( P4y ); + const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 ); + const double R2x( (Hx + R3x)*0.5 ), R2y( (Hy + R3y)*0.5 ); + const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 ); + const double L4x( R1x ), L4y( R1y ); + + // subdivide further + ++recursionDepth; + ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y); + ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y); + } + else + { + // requested resolution reached. + // Add end points to output iterator. + // order is preserved, since this is so to say depth first traversal. + *rPointIter++ = Point( FRound(P1x), FRound(P1y) ); + } +} + +// ======================================================================= + +void Polygon::AdaptiveSubdivide( Polygon& rResult, const double d ) const +{ + if( !mpImplPolygon->mpFlagAry ) + { + rResult = *this; + } + else + { + USHORT i; + USHORT nPts( GetSize() ); + ::std::vector< Point > aPoints; + aPoints.reserve( nPts ); + ::std::back_insert_iterator< ::std::vector< Point > > aPointIter( aPoints ); + + for(i=0; i<nPts;) + { + if( ( i + 3 ) < nPts ) + { + BYTE P1( mpImplPolygon->mpFlagAry[ i ] ); + BYTE P4( mpImplPolygon->mpFlagAry[ i + 3 ] ); + + if( ( POLY_NORMAL == P1 || POLY_SMOOTH == P1 || POLY_SYMMTR == P1 ) && + ( POLY_CONTROL == mpImplPolygon->mpFlagAry[ i + 1 ] ) && + ( POLY_CONTROL == mpImplPolygon->mpFlagAry[ i + 2 ] ) && + ( POLY_NORMAL == P4 || POLY_SMOOTH == P4 || POLY_SYMMTR == P4 ) ) + { + ImplAdaptiveSubdivide( aPointIter, d*d+1.0, 0, d*d, + mpImplPolygon->mpPointAry[ i ].X(), mpImplPolygon->mpPointAry[ i ].Y(), + mpImplPolygon->mpPointAry[ i+1 ].X(), mpImplPolygon->mpPointAry[ i+1 ].Y(), + mpImplPolygon->mpPointAry[ i+2 ].X(), mpImplPolygon->mpPointAry[ i+2 ].Y(), + mpImplPolygon->mpPointAry[ i+3 ].X(), mpImplPolygon->mpPointAry[ i+3 ].Y() ); + i += 3; + continue; + } + } + + *aPointIter++ = mpImplPolygon->mpPointAry[ i++ ]; + } + + // fill result polygon + rResult = Polygon( (USHORT)aPoints.size() ); // ensure sufficient size for copy + ::std::copy(aPoints.begin(), aPoints.end(), rResult.mpImplPolygon->mpPointAry); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::GetIntersection( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetIntersection( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::GetUnion( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetUnion( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::GetDifference( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetDifference( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::GetXOR( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetXOR( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::ImplReduceEdges( Polygon& rPoly, const double& rArea, USHORT nPercent ) +{ + const double fBound = 2000.0 * ( 100 - nPercent ) * 0.01; + USHORT nNumNoChange = 0, nNumRuns = 0; + + while( nNumNoChange < 2 ) + { + USHORT nPntCnt = rPoly.GetSize(), nNewPos = 0; + Polygon aNewPoly( nPntCnt ); + BOOL bChangeInThisRun = FALSE; + + for( USHORT n = 0; n < nPntCnt; n++ ) + { + BOOL bDeletePoint = FALSE; + + if( ( n + nNumRuns ) % 2 ) + { + USHORT nIndPrev = !n ? nPntCnt - 1 : n - 1; + USHORT nIndPrevPrev = !nIndPrev ? nPntCnt - 1 : nIndPrev - 1; + USHORT nIndNext = ( n == nPntCnt-1 ) ? 0 : n + 1; + USHORT nIndNextNext = ( nIndNext == nPntCnt - 1 ) ? 0 : nIndNext + 1; + Vector2D aVec1( rPoly[ nIndPrev ] ); aVec1 -= rPoly[ nIndPrevPrev ]; + Vector2D aVec2( rPoly[ n ] ); aVec2 -= rPoly[ nIndPrev ]; + Vector2D aVec3( rPoly[ nIndNext ] ); aVec3 -= rPoly[ n ]; + Vector2D aVec4( rPoly[ nIndNextNext ] ); aVec4 -= rPoly[ nIndNext ]; + double fDist1 = aVec1.GetLength(), fDist2 = aVec2.GetLength(); + double fDist3 = aVec3.GetLength(), fDist4 = aVec4.GetLength(); + double fTurnB = aVec2.Normalize().Scalar( aVec3.Normalize() ); + + if( fabs( fTurnB ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnB ) > ( 1.0 - SMALL_DVALUE ) ) + bDeletePoint = TRUE; + else + { + Vector2D aVecB( rPoly[ nIndNext ] ); + double fDistB = ( aVecB -= rPoly[ nIndPrev ] ).GetLength(); + double fLenWithB = fDist2 + fDist3; + double fLenFact = ( fDistB != 0.0 ) ? fLenWithB / fDistB : 1.0; + double fTurnPrev = aVec1.Normalize().Scalar( aVec2 ); + double fTurnNext = aVec3.Scalar( aVec4.Normalize() ); + double fGradPrev, fGradB, fGradNext; + + if( fabs( fTurnPrev ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnPrev ) > ( 1.0 - SMALL_DVALUE ) ) + fGradPrev = 0.0; + else + fGradPrev = acos( fTurnPrev ) / ( aVec1.IsNegative( aVec2 ) ? -F_PI180 : F_PI180 ); + + fGradB = acos( fTurnB ) / ( aVec2.IsNegative( aVec3 ) ? -F_PI180 : F_PI180 ); + + if( fabs( fTurnNext ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnNext ) > ( 1.0 - SMALL_DVALUE ) ) + fGradNext = 0.0; + else + fGradNext = acos( fTurnNext ) / ( aVec3.IsNegative( aVec4 ) ? -F_PI180 : F_PI180 ); + + if( ( fGradPrev > 0.0 && fGradB < 0.0 && fGradNext > 0.0 ) || + ( fGradPrev < 0.0 && fGradB > 0.0 && fGradNext < 0.0 ) ) + { + if( ( fLenFact < ( FSQRT2 + SMALL_DVALUE ) ) && + ( ( ( fDist1 + fDist4 ) / ( fDist2 + fDist3 ) ) * 2000.0 ) > fBound ) + { + bDeletePoint = TRUE; + } + } + else + { + double fRelLen = 1.0 - sqrt( fDistB / rArea ); + + if( fRelLen < 0.0 ) + fRelLen = 0.0; + else if( fRelLen > 1.0 ) + fRelLen = 1.0; + + if( ( (UINT32) ( ( ( fLenFact - 1.0 ) * 1000000.0 ) + 0.5 ) < fBound ) && + ( fabs( fGradB ) <= ( fRelLen * fBound * 0.01 ) ) ) + { + bDeletePoint = TRUE; + } + } + } + } + + if( !bDeletePoint ) + aNewPoly[ nNewPos++ ] = rPoly[ n ]; + else + bChangeInThisRun = TRUE; + } + + if( bChangeInThisRun && nNewPos ) + { + aNewPoly.SetSize( nNewPos ); + rPoly = aNewPoly; + nNumNoChange = 0; + } + else + nNumNoChange++; + + nNumRuns++; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Move( long nHorzMove, long nVertMove ) +{ + DBG_CHKTHIS( Polygon, NULL ); + + // Diese Abfrage sollte man fuer die DrawEngine durchfuehren + if ( !nHorzMove && !nVertMove ) + return; + + ImplMakeUnique(); + + // Punkte verschieben + USHORT nCount = mpImplPolygon->mnPoints; + for ( USHORT i = 0; i < nCount; i++ ) + { + Point* pPt = &(mpImplPolygon->mpPointAry[i]); + pPt->X() += nHorzMove; + pPt->Y() += nVertMove; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Translate(const Point& rTrans) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for ( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + mpImplPolygon->mpPointAry[ i ] += rTrans; +} + +// ----------------------------------------------------------------------- + +void Polygon::Scale( double fScaleX, double fScaleY ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for ( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[i]; + rPnt.X() = (long) ( fScaleX * rPnt.X() ); + rPnt.Y() = (long) ( fScaleY * rPnt.Y() ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Rotate( const Point& rCenter, USHORT nAngle10 ) +{ + DBG_CHKTHIS( Polygon, NULL ); + nAngle10 %= 3600; + + if( nAngle10 ) + { + const double fAngle = F_PI1800 * nAngle10; + Rotate( rCenter, sin( fAngle ), cos( fAngle ) ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Rotate( const Point& rCenter, double fSin, double fCos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + long nX, nY; + long nCenterX = rCenter.X(); + long nCenterY = rCenter.Y(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPt = mpImplPolygon->mpPointAry[ i ]; + + nX = rPt.X() - nCenterX; + nY = rPt.Y() - nCenterY; + rPt.X() = (long) FRound( fCos * nX + fSin * nY ) + nCenterX; + rPt.Y() = -(long) FRound( fSin * nX - fCos * nY ) + nCenterY; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::SlantX( long nYRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[ i ]; + const long nDy = rPnt.Y() - nYRef; + + rPnt.X() += (long)( fSin * nDy ); + rPnt.Y() = nYRef + (long)( fCos * nDy ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::SlantY( long nXRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[ i ]; + const long nDx = rPnt.X() - nXRef; + + rPnt.X() = nXRef + (long)( fCos * nDx ); + rPnt.Y() -= (long)( fSin * nDx ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Distort( const Rectangle& rRefRect, const Polygon& rDistortedRect ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + long Xr, Wr, X1, X2, X3, X4; + long Yr, Hr, Y1, Y2, Y3, Y4; + double fTx, fTy, fUx, fUy; + + Xr = rRefRect.Left(); + Yr = rRefRect.Top(); + Wr = rRefRect.GetWidth(); + Hr = rRefRect.GetHeight(); + + if( Wr && Hr ) + { + DBG_ASSERT( rDistortedRect.mpImplPolygon->mnPoints >= 4, "Distort rect too small!" ); + + X1 = rDistortedRect[0].X(); + Y1 = rDistortedRect[0].Y(); + X2 = rDistortedRect[1].X(); + Y2 = rDistortedRect[1].Y(); + X3 = rDistortedRect[3].X(); + Y3 = rDistortedRect[3].Y(); + X4 = rDistortedRect[2].X(); + Y4 = rDistortedRect[2].Y(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[ i ]; + + fTx = (double)( rPnt.X() - Xr) / Wr; + fTy = (double)( rPnt.Y() - Yr) / Hr; + fUx = 1.0 - fTx; + fUy = 1.0 - fTy; + + rPnt.X() = (long) ( fUy * (fUx * X1 + fTx * X2) + fTy * (fUx * X3 + fTx * X4) ); + rPnt.Y() = (long) ( fUx * (fUy * Y1 + fTy * Y3) + fTx * (fUy * Y2 + fTy * Y4) ); + } + } +} + +// ----------------------------------------------------------------------- + +class ImplPointFilter +{ +public: + virtual void LastPoint() = 0; + virtual void Input( const Point& rPoint ) = 0; +}; + +class ImplPolygonPointFilter : public ImplPointFilter +{ +public: + ImplPolygon* mpPoly; // Nicht loeschen, wird dem Polygon zugewiesen + USHORT mnSize; + + ImplPolygonPointFilter( USHORT nDestSize ) : + mnSize( 0 ) + { + mpPoly = new ImplPolygon( nDestSize ); + } + + virtual void LastPoint(); + virtual void Input( const Point& rPoint ); +}; + +void ImplPolygonPointFilter::Input( const Point& rPoint ) +{ + if ( !mnSize || (rPoint != mpPoly->mpPointAry[mnSize-1]) ) + { + mnSize++; + if ( mnSize > mpPoly->mnPoints ) + mpPoly->ImplSetSize( mnSize ); + mpPoly->mpPointAry[mnSize-1] = rPoint; + } +} + +void ImplPolygonPointFilter::LastPoint() +{ + if ( mnSize < mpPoly->mnPoints ) + mpPoly->ImplSetSize( mnSize ); +}; + +class ImplEdgePointFilter : public ImplPointFilter +{ + Point maFirstPoint; + Point maLastPoint; + ImplPointFilter& mrNextFilter; + const long mnLow; + const long mnHigh; + const int mnEdge; + int mnLastOutside; + BOOL mbFirst; + +public: + ImplEdgePointFilter( int nEdge, long nLow, long nHigh, + ImplPointFilter& rNextFilter ) : + mrNextFilter( rNextFilter ), + mnLow( nLow ), + mnHigh( nHigh ), + mnEdge( nEdge ), + mbFirst( TRUE ) + { + } + + Point EdgeSection( const Point& rPoint, int nEdge ) const; + int VisibleSide( const Point& rPoint ) const; + int IsPolygon() const + { return maFirstPoint == maLastPoint; } + + virtual void Input( const Point& rPoint ); + virtual void LastPoint(); +}; + +inline int ImplEdgePointFilter::VisibleSide( const Point& rPoint ) const +{ + if ( mnEdge & EDGE_HORZ ) + { + return rPoint.X() < mnLow ? EDGE_LEFT : + rPoint.X() > mnHigh ? EDGE_RIGHT : 0; + } + else + { + return rPoint.Y() < mnLow ? EDGE_TOP : + rPoint.Y() > mnHigh ? EDGE_BOTTOM : 0; + } +} + +Point ImplEdgePointFilter::EdgeSection( const Point& rPoint, int nEdge ) const +{ + long lx = maLastPoint.X(); + long ly = maLastPoint.Y(); + long md = rPoint.X() - lx; + long mn = rPoint.Y() - ly; + long nNewX; + long nNewY; + + if ( nEdge & EDGE_VERT ) + { + nNewY = (nEdge == EDGE_TOP) ? mnLow : mnHigh; + long dy = nNewY - ly; + if ( !md ) + nNewX = lx; + else if ( (LONG_MAX / Abs(md)) >= Abs(dy) ) + nNewX = (dy * md) / mn + lx; + else + { + BigInt ady = dy; + ady *= md; + if( ady.IsNeg() ) + if( mn < 0 ) + ady += mn/2; + else + ady -= (mn-1)/2; + else + if( mn < 0 ) + ady -= (mn+1)/2; + else + ady += mn/2; + ady /= mn; + nNewX = (long)ady + lx; + } + } + else + { + nNewX = (nEdge == EDGE_LEFT) ? mnLow : mnHigh; + long dx = nNewX - lx; + if ( !mn ) + nNewY = ly; + else if ( (LONG_MAX / Abs(mn)) >= Abs(dx) ) + nNewY = (dx * mn) / md + ly; + else + { + BigInt adx = dx; + adx *= mn; + if( adx.IsNeg() ) + if( md < 0 ) + adx += md/2; + else + adx -= (md-1)/2; + else + if( md < 0 ) + adx -= (md+1)/2; + else + adx += md/2; + adx /= md; + nNewY = (long)adx + ly; + } + } + + return Point( nNewX, nNewY ); +} + +void ImplEdgePointFilter::Input( const Point& rPoint ) +{ + int nOutside = VisibleSide( rPoint ); + + if ( mbFirst ) + { + maFirstPoint = rPoint; + mbFirst = FALSE; + if ( !nOutside ) + mrNextFilter.Input( rPoint ); + } + else if ( rPoint == maLastPoint ) + return; + else if ( !nOutside ) + { + if ( mnLastOutside ) + mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) ); + mrNextFilter.Input( rPoint ); + } + else if ( !mnLastOutside ) + mrNextFilter.Input( EdgeSection( rPoint, nOutside ) ); + else if ( nOutside != mnLastOutside ) + { + mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) ); + mrNextFilter.Input( EdgeSection( rPoint, nOutside ) ); + } + + maLastPoint = rPoint; + mnLastOutside = nOutside; +} + +void ImplEdgePointFilter::LastPoint() +{ + if ( !mbFirst ) + { + int nOutside = VisibleSide( maFirstPoint ); + + if ( nOutside != mnLastOutside ) + Input( maFirstPoint ); + mrNextFilter.LastPoint(); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Clip( const Rectangle& rRect, BOOL bPolygon ) +{ + // #105251# Justify rect befor edge filtering + Rectangle aJustifiedRect( rRect ); + aJustifiedRect.Justify(); + + USHORT nSourceSize = mpImplPolygon->mnPoints; + ImplPolygonPointFilter aPolygon( nSourceSize ); + ImplEdgePointFilter aHorzFilter( EDGE_HORZ, aJustifiedRect.Left(), aJustifiedRect.Right(), + aPolygon ); + ImplEdgePointFilter aVertFilter( EDGE_VERT, aJustifiedRect.Top(), aJustifiedRect.Bottom(), + aHorzFilter ); + + for ( USHORT i = 0; i < nSourceSize; i++ ) + aVertFilter.Input( mpImplPolygon->mpPointAry[i] ); + if ( bPolygon || aVertFilter.IsPolygon() ) + aVertFilter.LastPoint(); + else + aPolygon.LastPoint(); + + // Alte ImpPolygon-Daten loeschen und die vom ImpPolygonPointFilter + // zuweisen + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } + mpImplPolygon = aPolygon.mpPoly; +} + +// ----------------------------------------------------------------------- + +Rectangle Polygon::GetBoundRect() const +{ + DBG_CHKTHIS( Polygon, NULL ); + // Removing the assert. Bezier curves have the attribute that each single + // curve segment defined by four points can not exit the four-point polygon + // defined by that points. This allows to say that the curve segment can also + // never leave the Range of it's defining points. + // The result is that Polygon::GetBoundRect() may not create the minimal + // BoundRect of the Polygon (to get that, use basegfx::B2DPolygon classes), + // but will always create a valid BoundRect, at least as long as this method + // 'blindly' travels over all points, including control points. + // + // DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetBoundRect could fail with beziers!" ); + + USHORT nCount = mpImplPolygon->mnPoints; + if( ! nCount ) + return Rectangle(); + + long nXMin, nXMax, nYMin, nYMax; + + const Point* pPt = &(mpImplPolygon->mpPointAry[0]); + nXMin = nXMax = pPt->X(); + nYMin = nYMax = pPt->Y(); + + for ( USHORT i = 0; i < nCount; i++ ) + { + pPt = &(mpImplPolygon->mpPointAry[i]); + + if ( pPt->X() < nXMin ) + nXMin = pPt->X(); + if ( pPt->X() > nXMax ) + nXMax = pPt->X(); + if ( pPt->Y() < nYMin ) + nYMin = pPt->Y(); + if ( pPt->Y() > nYMax ) + nYMax = pPt->Y(); + } + + return Rectangle( nXMin, nYMin, nXMax, nYMax ); +} + +// ----------------------------------------------------------------------- + +double Polygon::GetArea() const +{ + const double fArea = GetSignedArea(); + return( ( fArea < 0.0 ) ? -fArea : fArea ); +} + +// ----------------------------------------------------------------------- + +double Polygon::GetSignedArea() const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetArea could fail with beziers!" ); + + double fArea = 0.0; + + if( mpImplPolygon->mnPoints > 2 ) + { + const USHORT nCount1 = mpImplPolygon->mnPoints - 1; + + for( USHORT i = 0; i < nCount1; ) + { + const Point& rPt = mpImplPolygon->mpPointAry[ i ]; + const Point& rPt1 = mpImplPolygon->mpPointAry[ ++i ]; + fArea += ( rPt.X() - rPt1.X() ) * ( rPt.Y() + rPt1.Y() ); + } + + const Point& rPt = mpImplPolygon->mpPointAry[ nCount1 ]; + const Point& rPt0 = mpImplPolygon->mpPointAry[ 0 ]; + fArea += ( rPt.X() - rPt0.X() ) * ( rPt.Y() + rPt0.Y() ); + } + + return fArea; +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsInside( const Point& rPoint ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "IsInside could fail with beziers!" ); + + const Rectangle aBound( GetBoundRect() ); + const Line aLine( rPoint, Point( aBound.Right() + 100L, rPoint.Y() ) ); + USHORT nCount = mpImplPolygon->mnPoints; + USHORT nPCounter = 0; + + if ( ( nCount > 2 ) && aBound.IsInside( rPoint ) ) + { + Point aPt1( mpImplPolygon->mpPointAry[ 0 ] ); + Point aIntersection; + Point aLastIntersection; + + while ( ( aPt1 == mpImplPolygon->mpPointAry[ nCount - 1 ] ) && ( nCount > 3 ) ) + nCount--; + + for ( USHORT i = 1; i <= nCount; i++ ) + { + const Point& rPt2 = mpImplPolygon->mpPointAry[ ( i < nCount ) ? i : 0 ]; + + if ( aLine.Intersection( Line( aPt1, rPt2 ), aIntersection ) ) + { + // Hiermit verhindern wir das Einfuegen von + // doppelten Intersections, die gleich hintereinander folgen + if ( nPCounter ) + { + if ( aIntersection != aLastIntersection ) + { + aLastIntersection = aIntersection; + nPCounter++; + } + } + else + { + aLastIntersection = aIntersection; + nPCounter++; + } + } + + aPt1 = rPt2; + } + } + + // innerhalb, wenn die Anzahl der Schnittpunkte ungerade ist + return ( ( nPCounter & 1 ) == 1 ); +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsRightOrientated() const +{ + DBG_CHKTHIS( Polygon, NULL ); + return GetSignedArea() >= 0.0; +} + +// ----------------------------------------------------------------------- + +void Polygon::Insert( USHORT nPos, const Point& rPt, PolyFlags eFlags ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + if( nPos >= mpImplPolygon->mnPoints ) + nPos = mpImplPolygon->mnPoints; + + mpImplPolygon->ImplSplit( nPos, 1 ); + mpImplPolygon->mpPointAry[ nPos ] = rPt; + + if( POLY_NORMAL != eFlags ) + { + mpImplPolygon->ImplCreateFlagArray(); + mpImplPolygon->mpFlagAry[ nPos ] = (BYTE) eFlags; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Insert( USHORT nPos, const Polygon& rPoly ) +{ + DBG_CHKTHIS( Polygon, NULL ); + const USHORT nInsertCount = rPoly.mpImplPolygon->mnPoints; + + if( nInsertCount ) + { + ImplMakeUnique(); + + if( nPos >= mpImplPolygon->mnPoints ) + nPos = mpImplPolygon->mnPoints; + + if( rPoly.mpImplPolygon->mpFlagAry ) + mpImplPolygon->ImplCreateFlagArray(); + + mpImplPolygon->ImplSplit( nPos, nInsertCount, rPoly.mpImplPolygon ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Remove( USHORT nPos, USHORT nCount ) +{ + DBG_CHKTHIS( Polygon, NULL ); + if( nCount && ( nPos < mpImplPolygon->mnPoints ) ) + { + ImplMakeUnique(); + mpImplPolygon->ImplRemove( nPos, nCount ); + } +} + +// ----------------------------------------------------------------------- + +Point& Polygon::operator[]( USHORT nPos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, "Polygon::[]: nPos >= nPoints" ); + + ImplMakeUnique(); + return mpImplPolygon->mpPointAry[nPos]; +} + +// ----------------------------------------------------------------------- + +Polygon& Polygon::operator=( const Polygon& rPoly ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERT( rPoly.mpImplPolygon->mnRefCount < 0xFFFFFFFE, "Polygon: RefCount overflow" ); + + // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann + // RefCount == 0 fuer statische Objekte + if ( rPoly.mpImplPolygon->mnRefCount ) + rPoly.mpImplPolygon->mnRefCount++; + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } + + mpImplPolygon = rPoly.mpImplPolygon; + return *this; +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::operator==( const Polygon& rPoly ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + + if ( (rPoly.mpImplPolygon == mpImplPolygon) ) + return TRUE; + else + return FALSE; +} + +// ----------------------------------------------------------------------- + +sal_Bool Polygon::IsEqual( const Polygon& rPoly ) const +{ + sal_Bool bIsEqual = sal_True;; + sal_uInt16 i; + if ( GetSize() != rPoly.GetSize() ) + bIsEqual = sal_False; + else + { + for ( i = 0; i < GetSize(); i++ ) + { + if ( ( GetPoint( i ) != rPoly.GetPoint( i ) ) || + ( GetFlags( i ) != rPoly.GetFlags( i ) ) ) + { + bIsEqual = sal_False; + break; + } + } + } + return bIsEqual; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStream, Polygon& rPoly ) +{ + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERTWARNING( rIStream.GetVersion(), "Polygon::>> - Solar-Version not set on rIStream" ); + + USHORT i; + USHORT nStart; + USHORT nCurPoints; + USHORT nPoints; + unsigned char bShort; + short nShortX; + short nShortY; + long nLongX; + long nLongY; + + // Anzahl der Punkte einlesen und Array erzeugen + rIStream >> nPoints; + if ( rPoly.mpImplPolygon->mnRefCount != 1 ) + { + if ( rPoly.mpImplPolygon->mnRefCount ) + rPoly.mpImplPolygon->mnRefCount--; + rPoly.mpImplPolygon = new ImplPolygon( nPoints ); + } + else + rPoly.mpImplPolygon->ImplSetSize( nPoints, FALSE ); + + // Je nach CompressMode das Polygon einlesen + if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + i = 0; + while ( i < nPoints ) + { + rIStream >> bShort >> nCurPoints; + + if ( bShort ) + { + for ( nStart = i; i < nStart+nCurPoints; i++ ) + { + rIStream >> nShortX >> nShortY; + rPoly.mpImplPolygon->mpPointAry[i].X() = nShortX; + rPoly.mpImplPolygon->mpPointAry[i].Y() = nShortY; + } + } + else + { + for ( nStart = i; i < nStart+nCurPoints; i++ ) + { + rIStream >> nLongX >> nLongY; + rPoly.mpImplPolygon->mpPointAry[i].X() = nLongX; + rPoly.mpImplPolygon->mpPointAry[i].Y() = nLongY; + } + } + } + } + else + { + // Feststellen, ob ueber die Operatoren geschrieben werden muss +#if (SAL_TYPES_SIZEOFLONG) != 4 + if ( 1 ) +#else +#ifdef OSL_BIGENDIAN + if ( rIStream.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN ) +#else + if ( rIStream.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN ) +#endif +#endif + { + for( i = 0; i < nPoints; i++ ) + { + rIStream >> rPoly.mpImplPolygon->mpPointAry[i].X() + >> rPoly.mpImplPolygon->mpPointAry[i].Y(); + } + } + else + rIStream.Read( rPoly.mpImplPolygon->mpPointAry, nPoints*sizeof(Point) ); + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const Polygon& rPoly ) +{ + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERTWARNING( rOStream.GetVersion(), "Polygon::<< - Solar-Version not set on rOStream" ); + + unsigned char bShort; + unsigned char bCurShort; + USHORT nStart; + USHORT i; + USHORT nPoints = rPoly.GetSize(); + + // Anzahl der Punkte rausschreiben + rOStream << nPoints; + + // Je nach CompressMode das Polygon rausschreiben + if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + i = 0; + while ( i < nPoints ) + { + nStart = i; + + // Feststellen, welcher Typ geschrieben werden soll + if ( ((rPoly.mpImplPolygon->mpPointAry[nStart].X() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].X() <= SHRT_MAX)) && + ((rPoly.mpImplPolygon->mpPointAry[nStart].Y() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].Y() <= SHRT_MAX)) ) + bShort = TRUE; + else + bShort = FALSE; + while ( i < nPoints ) + { + // Feststellen, welcher Typ geschrieben werden soll + if ( ((rPoly.mpImplPolygon->mpPointAry[nStart].X() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].X() <= SHRT_MAX)) && + ((rPoly.mpImplPolygon->mpPointAry[nStart].Y() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].Y() <= SHRT_MAX)) ) + bCurShort = TRUE; + else + bCurShort = FALSE; + + // Wenn sich die Werte in einen anderen Bereich begeben, + // muessen wir neu rausschreiben + if ( bCurShort != bShort ) + { + bShort = bCurShort; + break; + } + + i++; + } + + rOStream << bShort << (USHORT)(i-nStart); + + if ( bShort ) + { + for( ; nStart < i; nStart++ ) + { + rOStream << (short)rPoly.mpImplPolygon->mpPointAry[nStart].X() + << (short)rPoly.mpImplPolygon->mpPointAry[nStart].Y(); + } + } + else + { + for( ; nStart < i; nStart++ ) + { + rOStream << rPoly.mpImplPolygon->mpPointAry[nStart].X() + << rPoly.mpImplPolygon->mpPointAry[nStart].Y(); + } + } + } + } + else + { + // Feststellen, ob ueber die Operatoren geschrieben werden muss +#if (SAL_TYPES_SIZEOFLONG) != 4 + if ( 1 ) +#else +#ifdef OSL_BIGENDIAN + if ( rOStream.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN ) +#else + if ( rOStream.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN ) +#endif +#endif + { + for( i = 0; i < nPoints; i++ ) + { + rOStream << rPoly.mpImplPolygon->mpPointAry[i].X() + << rPoly.mpImplPolygon->mpPointAry[i].Y(); + } + } + else + { + if ( nPoints ) + rOStream.Write( rPoly.mpImplPolygon->mpPointAry, nPoints*sizeof(Point) ); + } + } + + return rOStream; +} + +// ----------------------------------------------------------------------- + +void Polygon::ImplRead( SvStream& rIStream ) +{ + sal_uInt8 bHasPolyFlags; + + rIStream >> *this + >> bHasPolyFlags; + + if ( bHasPolyFlags ) + { + mpImplPolygon->mpFlagAry = new sal_uInt8[ mpImplPolygon->mnPoints ]; + rIStream.Read( mpImplPolygon->mpFlagAry, mpImplPolygon->mnPoints ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Read( SvStream& rIStream ) +{ + VersionCompat aCompat( rIStream, STREAM_READ ); + + ImplRead( rIStream ); +} + +// ----------------------------------------------------------------------- + +void Polygon::ImplWrite( SvStream& rOStream ) const +{ + sal_uInt8 bHasPolyFlags = mpImplPolygon->mpFlagAry != NULL; + rOStream << *this + << bHasPolyFlags; + + if ( bHasPolyFlags ) + rOStream.Write( mpImplPolygon->mpFlagAry, mpImplPolygon->mnPoints ); +} + +// ----------------------------------------------------------------------- + +void Polygon::Write( SvStream& rOStream ) const +{ + VersionCompat aCompat( rOStream, STREAM_WRITE, 1 ); + + ImplWrite( rOStream ); +} + +// ----------------------------------------------------------------------- +// #i74631# numerical correction method for B2DPolygon +void impCorrectContinuity(basegfx::B2DPolygon& roPolygon, sal_uInt32 nIndex, BYTE nCFlag) +{ + const sal_uInt32 nPointCount(roPolygon.count()); + OSL_ENSURE(nIndex < nPointCount, "impCorrectContinuity: index access out of range (!)"); + + if(nIndex < nPointCount && (POLY_SMOOTH == nCFlag || POLY_SYMMTR == nCFlag)) + { + if(roPolygon.isPrevControlPointUsed(nIndex) && roPolygon.isNextControlPointUsed(nIndex)) + { + const basegfx::B2DPoint aPoint(roPolygon.getB2DPoint(nIndex)); + + if(POLY_SMOOTH == nCFlag) + { + // C1: apply inverse direction of prev to next, keep length of next + const basegfx::B2DVector aOriginalNext(roPolygon.getNextControlPoint(nIndex) - aPoint); + basegfx::B2DVector aNewNext(aPoint - roPolygon.getPrevControlPoint(nIndex)); + + aNewNext.setLength(aOriginalNext.getLength()); + roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + aNewNext)); + } + else // POLY_SYMMTR + { + // C2: apply inverse control point to next + roPolygon.setNextControlPoint(nIndex, (2.0 * aPoint) - roPolygon.getPrevControlPoint(nIndex)); + } + } + } +} + +// ----------------------------------------------------------------------- +// convert to basegfx::B2DPolygon and return +basegfx::B2DPolygon Polygon::getB2DPolygon() const +{ + basegfx::B2DPolygon aRetval; + const sal_uInt16 nCount(mpImplPolygon->mnPoints); + + if(nCount) + { + if(mpImplPolygon->mpFlagAry) + { + // handling for curves. Add start point + const Point aStartPoint(mpImplPolygon->mpPointAry[0]); + BYTE nPointFlag(mpImplPolygon->mpFlagAry[0]); + aRetval.append(basegfx::B2DPoint(aStartPoint.X(), aStartPoint.Y())); + Point aControlA, aControlB; + + for(sal_uInt16 a(1); a < nCount;) + { + bool bControlA(false); + bool bControlB(false); + + if(POLY_CONTROL == mpImplPolygon->mpFlagAry[a]) + { + aControlA = mpImplPolygon->mpPointAry[a++]; + bControlA = true; + } + + if(a < nCount && POLY_CONTROL == mpImplPolygon->mpFlagAry[a]) + { + aControlB = mpImplPolygon->mpPointAry[a++]; + bControlB = true; + } + + // assert invalid polygons + OSL_ENSURE(bControlA == bControlB, "Polygon::getB2DPolygon: Invalid source polygon (!)"); + + if(a < nCount) + { + const Point aEndPoint(mpImplPolygon->mpPointAry[a]); + + if(bControlA) + { + // bezier edge, add + aRetval.appendBezierSegment( + basegfx::B2DPoint(aControlA.X(), aControlA.Y()), + basegfx::B2DPoint(aControlB.X(), aControlB.Y()), + basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y())); + + impCorrectContinuity(aRetval, aRetval.count() - 2, nPointFlag); + } + else + { + // no bezier edge, add end point + aRetval.append(basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y())); + } + + nPointFlag = mpImplPolygon->mpFlagAry[a++]; + } + } + + // if exist, remove double first/last points, set closed and correct control points + basegfx::tools::checkClosed(aRetval); + + if(aRetval.isClosed()) + { + // closeWithGeometryChange did really close, so last point(s) were removed. + // Correct the continuity in the changed point + impCorrectContinuity(aRetval, 0, mpImplPolygon->mpFlagAry[0]); + } + } + else + { + // extra handling for non-curves (most-used case) for speedup + for(sal_uInt16 a(0); a < nCount; a++) + { + // get point and add + const Point aPoint(mpImplPolygon->mpPointAry[a]); + aRetval.append(basegfx::B2DPoint(aPoint.X(), aPoint.Y())); + } + + // set closed flag + basegfx::tools::checkClosed(aRetval); + } + } + + return aRetval; +} + +// ----------------------------------------------------------------------- +// constructor to convert from basegfx::B2DPolygon +// #i76891# Needed to change from adding all control points (even for unused +// edges) and creating a fixed-size Polygon in the first run to creating the +// minimal Polygon. This requires a temporary Point- and Flag-Array for curves +// and a memcopy at ImplPolygon creation, but contains no zero-controlpoints +// for straight edges. +Polygon::Polygon(const basegfx::B2DPolygon& rPolygon) +: mpImplPolygon(0) +{ + DBG_CTOR( Polygon, NULL ); + + const bool bCurve(rPolygon.areControlPointsUsed()); + const bool bClosed(rPolygon.isClosed()); + sal_uInt32 nB2DLocalCount(rPolygon.count()); + + if(bCurve) + { + // #127979# Reduce source point count hard to the limit of the tools Polygon + if(nB2DLocalCount > ((0x0000ffff / 3L) - 1L)) + { + DBG_ERROR("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"); + nB2DLocalCount = ((0x0000ffff / 3L) - 1L); + } + + // calculate target point count + const sal_uInt32 nLoopCount(bClosed ? nB2DLocalCount : (nB2DLocalCount ? nB2DLocalCount - 1L : 0L )); + + if(nLoopCount) + { + // calculate maximum array size and allocate; prepare insert index + const sal_uInt32 nMaxTargetCount((nLoopCount * 3) + 1); + mpImplPolygon = new ImplPolygon(static_cast< sal_uInt16 >(nMaxTargetCount), true); + + // prepare insert index and current point + sal_uInt32 nArrayInsert(0); + basegfx::B2DCubicBezier aBezier; + aBezier.setStartPoint(rPolygon.getB2DPoint(0)); + + for(sal_uInt32 a(0L); a < nLoopCount; a++) + { + // add current point (always) and remember StartPointIndex for evtl. later corrections + const Point aStartPoint(FRound(aBezier.getStartPoint().getX()), FRound(aBezier.getStartPoint().getY())); + const sal_uInt32 nStartPointIndex(nArrayInsert); + mpImplPolygon->mpPointAry[nStartPointIndex] = aStartPoint; + mpImplPolygon->mpFlagAry[nStartPointIndex] = (BYTE)POLY_NORMAL; + nArrayInsert++; + + // prepare next segment + const sal_uInt32 nNextIndex((a + 1) % nB2DLocalCount); + aBezier.setEndPoint(rPolygon.getB2DPoint(nNextIndex)); + aBezier.setControlPointA(rPolygon.getNextControlPoint(a)); + aBezier.setControlPointB(rPolygon.getPrevControlPoint(nNextIndex)); + + if(aBezier.isBezier()) + { + // if one is used, add always two control points due to the old schema + mpImplPolygon->mpPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointA().getX()), FRound(aBezier.getControlPointA().getY())); + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_CONTROL; + nArrayInsert++; + + mpImplPolygon->mpPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointB().getX()), FRound(aBezier.getControlPointB().getY())); + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_CONTROL; + nArrayInsert++; + } + + // test continuity with previous control point to set flag value + if(aBezier.getControlPointA() != aBezier.getStartPoint() && (bClosed || a)) + { + const basegfx::B2VectorContinuity eCont(rPolygon.getContinuityInPoint(a)); + + if(basegfx::CONTINUITY_C1 == eCont) + { + mpImplPolygon->mpFlagAry[nStartPointIndex] = (BYTE)POLY_SMOOTH; + } + else if(basegfx::CONTINUITY_C2 == eCont) + { + mpImplPolygon->mpFlagAry[nStartPointIndex] = (BYTE)POLY_SYMMTR; + } + } + + // prepare next polygon step + aBezier.setStartPoint(aBezier.getEndPoint()); + } + + if(bClosed) + { + // add first point again as closing point due to old definition + mpImplPolygon->mpPointAry[nArrayInsert] = mpImplPolygon->mpPointAry[0]; + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_NORMAL; + nArrayInsert++; + } + else + { + // add last point as closing point + const basegfx::B2DPoint aClosingPoint(rPolygon.getB2DPoint(nB2DLocalCount - 1L)); + const Point aEnd(FRound(aClosingPoint.getX()), FRound(aClosingPoint.getY())); + mpImplPolygon->mpPointAry[nArrayInsert] = aEnd; + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_NORMAL; + nArrayInsert++; + } + + DBG_ASSERT(nArrayInsert <= nMaxTargetCount, "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)"); + + if(nArrayInsert != nMaxTargetCount) + { + mpImplPolygon->ImplSetSize(static_cast< sal_uInt16 >(nArrayInsert), true); + } + } + } + else + { + // #127979# Reduce source point count hard to the limit of the tools Polygon + if(nB2DLocalCount > (0x0000ffff - 1L)) + { + DBG_ERROR("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"); + nB2DLocalCount = (0x0000ffff - 1L); + } + + if(nB2DLocalCount) + { + // point list creation + const sal_uInt32 nTargetCount(nB2DLocalCount + (bClosed ? 1L : 0L)); + mpImplPolygon = new ImplPolygon( static_cast< sal_uInt16 >(nTargetCount) ); + sal_uInt16 nIndex(0); + + for(sal_uInt32 a(0L); a < nB2DLocalCount; a++) + { + basegfx::B2DPoint aB2DPoint(rPolygon.getB2DPoint(a)); + Point aPoint(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY())); + mpImplPolygon->mpPointAry[nIndex++] = aPoint; + } + + if(bClosed) + { + // add first point as closing point + mpImplPolygon->mpPointAry[nIndex] = mpImplPolygon->mpPointAry[0]; + } + } + } + + if(!mpImplPolygon) + { + // no content yet, create empty polygon + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); + } +} + +// eof |