| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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( sal_uInt16 nInitSize, sal_Bool bFlags ) |
| { |
| if ( nInitSize ) |
| { |
| mpPointAry = (Point*)new char[(sal_uIntPtr)nInitSize*sizeof(Point)]; |
| memset( mpPointAry, 0, (sal_uIntPtr)nInitSize*sizeof(Point) ); |
| } |
| else |
| mpPointAry = NULL; |
| |
| if( bFlags ) |
| { |
| mpFlagAry = new sal_uInt8[ nInitSize ]; |
| memset( mpPointAry, 0, nInitSize ); |
| } |
| else |
| mpFlagAry = NULL; |
| |
| mnRefCount = 1; |
| mnPoints = nInitSize; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| ImplPolygon::ImplPolygon( const ImplPolygon& rImpPoly ) |
| { |
| if ( rImpPoly.mnPoints ) |
| { |
| mpPointAry = (Point*)new char[(sal_uIntPtr)rImpPoly.mnPoints*sizeof(Point)]; |
| memcpy( mpPointAry, rImpPoly.mpPointAry, (sal_uIntPtr)rImpPoly.mnPoints*sizeof(Point) ); |
| |
| if( rImpPoly.mpFlagAry ) |
| { |
| mpFlagAry = new sal_uInt8[ rImpPoly.mnPoints ]; |
| memcpy( mpFlagAry, rImpPoly.mpFlagAry, rImpPoly.mnPoints ); |
| } |
| else |
| mpFlagAry = NULL; |
| } |
| else |
| { |
| mpPointAry = NULL; |
| mpFlagAry = NULL; |
| } |
| |
| mnRefCount = 1; |
| mnPoints = rImpPoly.mnPoints; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| ImplPolygon::ImplPolygon( sal_uInt16 nInitSize, const Point* pInitAry, const sal_uInt8* pInitFlags ) |
| { |
| if ( nInitSize ) |
| { |
| mpPointAry = (Point*)new char[(sal_uIntPtr)nInitSize*sizeof(Point)]; |
| memcpy( mpPointAry, pInitAry, (sal_uIntPtr)nInitSize*sizeof( Point ) ); |
| |
| if( pInitFlags ) |
| { |
| mpFlagAry = new sal_uInt8[ 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( sal_uInt16 nNewSize, sal_Bool bResize ) |
| { |
| if( mnPoints == nNewSize ) |
| return; |
| |
| Point* pNewAry; |
| |
| if ( nNewSize ) |
| { |
| pNewAry = (Point*)new char[(sal_uIntPtr)nNewSize*sizeof(Point)]; |
| |
| if ( bResize ) |
| { |
| // Alte Punkte kopieren |
| if ( mnPoints < nNewSize ) |
| { |
| // Neue Punkte mit 0 initialisieren |
| memset( pNewAry+mnPoints, 0, (sal_uIntPtr)(nNewSize-mnPoints)*sizeof(Point) ); |
| if ( mpPointAry ) |
| memcpy( pNewAry, mpPointAry, mnPoints*sizeof(Point) ); |
| } |
| else |
| { |
| if ( mpPointAry ) |
| memcpy( pNewAry, mpPointAry, (sal_uIntPtr)nNewSize*sizeof(Point) ); |
| } |
| } |
| } |
| else |
| pNewAry = NULL; |
| |
| if ( mpPointAry ) |
| delete[] (char*) mpPointAry; |
| |
| // ggf. FlagArray beruecksichtigen |
| if( mpFlagAry ) |
| { |
| sal_uInt8* pNewFlagAry; |
| |
| if( nNewSize ) |
| { |
| pNewFlagAry = new sal_uInt8[ 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( sal_uInt16 nPos, sal_uInt16 nSpace, ImplPolygon* pInitPoly ) |
| { |
| const sal_uIntPtr nSpaceSize = nSpace * sizeof( Point ); |
| |
| //Can't fit this in :-(, throw ? |
| if (mnPoints + nSpace > USHRT_MAX) |
| return; |
| |
| const sal_uInt16 nNewSize = mnPoints + nSpace; |
| |
| if( nPos >= mnPoints ) |
| { |
| // Hinten anhaengen |
| nPos = mnPoints; |
| ImplSetSize( nNewSize, sal_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 sal_uInt16 nSecPos = nPos + nSpace; |
| const sal_uInt16 nRest = mnPoints - nPos; |
| |
| Point* pNewAry = (Point*) new char[ (sal_uIntPtr) 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 ) |
| { |
| sal_uInt8* pNewFlagAry = new sal_uInt8[ 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( sal_uInt16 nPos, sal_uInt16 nCount ) |
| { |
| const sal_uInt16 nRemoveCount = Min( (sal_uInt16) ( mnPoints - nPos ), (sal_uInt16) nCount ); |
| |
| if( nRemoveCount ) |
| { |
| const sal_uInt16 nNewSize = mnPoints - nRemoveCount; |
| const sal_uInt16 nSecPos = nPos + nRemoveCount; |
| const sal_uInt16 nRest = mnPoints - nSecPos; |
| |
| Point* pNewAry = (Point*) new char[ (sal_uIntPtr) 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 ) |
| { |
| sal_uInt8* pNewFlagAry = new sal_uInt8[ 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 sal_uInt8[ 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( sal_uInt16 nSize ) |
| { |
| DBG_CTOR( Polygon, NULL ); |
| |
| if ( nSize ) |
| mpImplPolygon = new ImplPolygon( nSize ); |
| else |
| mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| Polygon::Polygon( sal_uInt16 nPoints, const Point* pPtAry, const sal_uInt8* 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, sal_uIntPtr nHorzRound, sal_uIntPtr nVertRound ) |
| { |
| DBG_CTOR( Polygon, NULL ); |
| |
| if ( rRect.IsEmpty() ) |
| mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); |
| else |
| { |
| Rectangle aRect( rRect ); |
| aRect.Justify(); // SJ: i9140 |
| |
| nHorzRound = Min( nHorzRound, (sal_uIntPtr) labs( aRect.GetWidth() >> 1 ) ); |
| nVertRound = Min( nVertRound, (sal_uIntPtr) 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 ); |
| sal_uInt16 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, sal_uInt16 nPoints ) |
| { |
| DBG_CTOR( Polygon, NULL ); |
| |
| if( nRadX && nRadY ) |
| { |
| // Default berechnen (abhaengig von Groesse) |
| if( !nPoints ) |
| { |
| nPoints = (sal_uInt16) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - |
| sqrt( (double) labs( nRadX * nRadY ) ) ) ); |
| |
| nPoints = (sal_uInt16) 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; |
| sal_uInt16 i; |
| sal_uInt16 nPoints2 = nPoints >> 1; |
| sal_uInt16 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(); |
| sal_uInt16 nPoints; |
| |
| nPoints = (sal_uInt16) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - |
| sqrt( (double) labs( nRadX * nRadY ) ) ) ); |
| |
| nPoints = (sal_uInt16) 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; |
| sal_uInt16 nStart; |
| sal_uInt16 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( (sal_uInt16) ( ( fDiff * 0.1591549 ) * nPoints ), (sal_uInt16) 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, |
| sal_uInt16 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( sal_uInt16 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; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_uInt8* 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 sal_uInt8* Polygon::GetConstFlagAry() const |
| { |
| DBG_CHKTHIS( Polygon, NULL ); |
| return mpImplPolygon->mpFlagAry; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void Polygon::SetPoint( const Point& rPt, sal_uInt16 nPos ) |
| { |
| DBG_CHKTHIS( Polygon, NULL ); |
| DBG_ASSERT( nPos < mpImplPolygon->mnPoints, |
| "Polygon::SetPoint(): nPos >= nPoints" ); |
| |
| ImplMakeUnique(); |
| mpImplPolygon->mpPointAry[nPos] = rPt; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void Polygon::SetFlags( sal_uInt16 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 ] = (sal_uInt8) eFlags; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| const Point& Polygon::GetPoint( sal_uInt16 nPos ) const |
| { |
| DBG_CHKTHIS( Polygon, NULL ); |
| DBG_ASSERT( nPos < mpImplPolygon->mnPoints, |
| "Polygon::GetPoint(): nPos >= nPoints" ); |
| |
| return mpImplPolygon->mpPointAry[nPos]; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| PolyFlags Polygon::GetFlags( sal_uInt16 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; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_Bool Polygon::IsControl(sal_uInt16 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 ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_Bool Polygon::IsSmooth(sal_uInt16 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 ) ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_Bool Polygon::IsRect() const |
| { |
| sal_Bool bIsRect = sal_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 = sal_True; |
| } |
| } |
| return bIsRect; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void Polygon::SetSize( sal_uInt16 nNewSize ) |
| { |
| DBG_CHKTHIS( Polygon, NULL ); |
| |
| if( nNewSize != mpImplPolygon->mnPoints ) |
| { |
| ImplMakeUnique(); |
| mpImplPolygon->ImplSetSize( nNewSize ); |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_uInt16 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( sal_uInt16 nP1, sal_uInt16 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( sal_uIntPtr nOptimizeFlags, const PolyOptimizeData* pData ) |
| { |
| DBG_CHKTHIS( Polygon, NULL ); |
| DBG_ASSERT( !mpImplPolygon->mpFlagAry, "Optimizing could fail with beziers!" ); |
| |
| sal_uInt16 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 sal_uInt16 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 ]; |
| sal_uIntPtr 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 ) |
| { |
| sal_uInt16 nLast = 0, nNewCount = 1; |
| |
| aNewPoly.SetSize( nSize ); |
| aNewPoly[ 0 ] = rFirst; |
| |
| for( sal_uInt16 i = 1; i < nSize; i++ ) |
| { |
| if( ( mpImplPolygon->mpPointAry[ i ] != mpImplPolygon->mpPointAry[ nLast ] ) && |
| ( !nReduce || ( nReduce < (sal_uIntPtr) 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 |
| { |
| sal_uInt16 i; |
| sal_uInt16 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 ) |
| { |
| sal_uInt8 P1( mpImplPolygon->mpFlagAry[ i ] ); |
| sal_uInt8 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++ ]; |
| |
| if (aPoints.size() >= SAL_MAX_UINT16) |
| { |
| OSL_ENSURE(aPoints.size() < SAL_MAX_UINT16, |
| "Polygon::AdapativeSubdivision created polygon too many points;" |
| " using original polygon instead"); |
| |
| // The resulting polygon can not hold all the points |
| // that we have created so far. Stop the subdivision |
| // and return a copy of the unmodified polygon. |
| rResult = *this; |
| return; |
| } |
| } |
| |
| // fill result polygon |
| rResult = Polygon( (sal_uInt16)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, sal_uInt16 nPercent ) |
| { |
| const double fBound = 2000.0 * ( 100 - nPercent ) * 0.01; |
| sal_uInt16 nNumNoChange = 0, nNumRuns = 0; |
| |
| while( nNumNoChange < 2 ) |
| { |
| sal_uInt16 nPntCnt = rPoly.GetSize(), nNewPos = 0; |
| Polygon aNewPoly( nPntCnt ); |
| sal_Bool bChangeInThisRun = sal_False; |
| |
| for( sal_uInt16 n = 0; n < nPntCnt; n++ ) |
| { |
| sal_Bool bDeletePoint = sal_False; |
| |
| if( ( n + nNumRuns ) % 2 ) |
| { |
| sal_uInt16 nIndPrev = !n ? nPntCnt - 1 : n - 1; |
| sal_uInt16 nIndPrevPrev = !nIndPrev ? nPntCnt - 1 : nIndPrev - 1; |
| sal_uInt16 nIndNext = ( n == nPntCnt-1 ) ? 0 : n + 1; |
| sal_uInt16 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 = sal_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 = sal_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( ( (sal_uInt32) ( ( ( fLenFact - 1.0 ) * 1000000.0 ) + 0.5 ) < fBound ) && |
| ( fabs( fGradB ) <= ( fRelLen * fBound * 0.01 ) ) ) |
| { |
| bDeletePoint = sal_True; |
| } |
| } |
| } |
| } |
| |
| if( !bDeletePoint ) |
| aNewPoly[ nNewPos++ ] = rPoly[ n ]; |
| else |
| bChangeInThisRun = sal_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 |
| sal_uInt16 nCount = mpImplPolygon->mnPoints; |
| for ( sal_uInt16 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 ( sal_uInt16 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 ( sal_uInt16 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, sal_uInt16 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( sal_uInt16 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( sal_uInt16 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( sal_uInt16 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( sal_uInt16 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 |
| sal_uInt16 mnSize; |
| |
| ImplPolygonPointFilter( sal_uInt16 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; |
| sal_Bool mbFirst; |
| |
| public: |
| ImplEdgePointFilter( int nEdge, long nLow, long nHigh, |
| ImplPointFilter& rNextFilter ) : |
| mrNextFilter( rNextFilter ), |
| mnLow( nLow ), |
| mnHigh( nHigh ), |
| mnEdge( nEdge ), |
| mbFirst( sal_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 = sal_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, sal_Bool bPolygon ) |
| { |
| // #105251# Justify rect befor edge filtering |
| Rectangle aJustifiedRect( rRect ); |
| aJustifiedRect.Justify(); |
| |
| sal_uInt16 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 ( sal_uInt16 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!" ); |
| |
| sal_uInt16 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 ( sal_uInt16 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 sal_uInt16 nCount1 = mpImplPolygon->mnPoints - 1; |
| |
| for( sal_uInt16 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; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_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() ) ); |
| sal_uInt16 nCount = mpImplPolygon->mnPoints; |
| sal_uInt16 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 ( sal_uInt16 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 ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_Bool Polygon::IsRightOrientated() const |
| { |
| DBG_CHKTHIS( Polygon, NULL ); |
| return GetSignedArea() >= 0.0; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void Polygon::Insert( sal_uInt16 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 ] = (sal_uInt8) eFlags; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void Polygon::Insert( sal_uInt16 nPos, const Polygon& rPoly ) |
| { |
| DBG_CHKTHIS( Polygon, NULL ); |
| const sal_uInt16 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( sal_uInt16 nPos, sal_uInt16 nCount ) |
| { |
| DBG_CHKTHIS( Polygon, NULL ); |
| if( nCount && ( nPos < mpImplPolygon->mnPoints ) ) |
| { |
| ImplMakeUnique(); |
| mpImplPolygon->ImplRemove( nPos, nCount ); |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| Point& Polygon::operator[]( sal_uInt16 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; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_Bool Polygon::operator==( const Polygon& rPoly ) const |
| { |
| DBG_CHKTHIS( Polygon, NULL ); |
| DBG_CHKOBJ( &rPoly, Polygon, NULL ); |
| |
| if ( (rPoly.mpImplPolygon == mpImplPolygon) ) |
| return sal_True; |
| else |
| return sal_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" ); |
| |
| sal_uInt16 i; |
| sal_uInt16 nStart; |
| sal_uInt16 nCurPoints; |
| sal_uInt16 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, sal_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; |
| sal_uInt16 nStart; |
| sal_uInt16 i; |
| sal_uInt16 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 = sal_True; |
| else |
| bShort = sal_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 = sal_True; |
| else |
| bCurShort = sal_False; |
| |
| // Wenn sich die Werte in einen anderen Bereich begeben, |
| // muessen wir neu rausschreiben |
| if ( bCurShort != bShort ) |
| { |
| bShort = bCurShort; |
| break; |
| } |
| |
| i++; |
| } |
| |
| rOStream << bShort << (sal_uInt16)(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 ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // numerical correction method for B2DPolygon |
| void impCorrectContinuity(basegfx::B2DPolygon& roPolygon, sal_uInt32 nIndex, sal_uInt8 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)) |
| { |
| // #115917# Patch from osnola (modified, thanks for showing the porblem) |
| // |
| // The correction is needed because an integer polygon with control points |
| // is converted to double precision. When C1 or C2 is used the involved vectors |
| // may not have the same directions/lengths since these come from integer coordinates |
| // and may have been snapped to different nearest integer coordinates. The snap error |
| // is in the range of +-1 in y and y, thus 0.0 <= error <= sqrt(2.0). Nonetheless, |
| // it needs to be corrected to be able to detect the continuity in this points |
| // correctly. |
| // |
| // We only have the integer data here (already in double precision form, but no mantisses |
| // used), so the best correction is to use: |
| // |
| // for C1: The longest vector since it potentially has best preserved the original vector. |
| // Even better the sum of the vectors, weighted by their length. This gives the |
| // normal vector addition to get the vector itself, lengths need to be preserved. |
| // for C2: The mediated vector(s) since both should be the same, but mirrored |
| |
| // extract the point and vectors |
| const basegfx::B2DPoint aPoint(roPolygon.getB2DPoint(nIndex)); |
| const basegfx::B2DVector aNext(roPolygon.getNextControlPoint(nIndex) - aPoint); |
| const basegfx::B2DVector aPrev(aPoint - roPolygon.getPrevControlPoint(nIndex)); |
| |
| // calculate common direction vector, normalize |
| const basegfx::B2DVector aDirection(aNext + aPrev); |
| |
| if(POLY_SMOOTH == nCFlag) |
| { |
| // C1: apply common direction vector, preserve individual lengths |
| const double fInvDirectionLen(1.0 / aDirection.getLength()); |
| roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + (aDirection * (aNext.getLength() * fInvDirectionLen)))); |
| roPolygon.setPrevControlPoint(nIndex, basegfx::B2DPoint(aPoint - (aDirection * (aPrev.getLength() * fInvDirectionLen)))); |
| } |
| else // POLY_SYMMTR |
| { |
| // C2: get mediated length. Taking half of the unnormalized direction would be |
| // an approximation, but not correct. |
| const double fMedLength((aNext.getLength() + aPrev.getLength()) * (0.5 / aDirection.getLength())); |
| const basegfx::B2DVector aScaledDirection(aDirection * fMedLength); |
| |
| // Bring Direction to correct length and apply |
| roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + aScaledDirection)); |
| roPolygon.setPrevControlPoint(nIndex, basegfx::B2DPoint(aPoint - aScaledDirection)); |
| } |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // 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]); |
| sal_uInt8 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] = (sal_uInt8)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] = (sal_uInt8)POLY_CONTROL; |
| nArrayInsert++; |
| |
| mpImplPolygon->mpPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointB().getX()), FRound(aBezier.getControlPointB().getY())); |
| mpImplPolygon->mpFlagAry[nArrayInsert] = (sal_uInt8)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] = (sal_uInt8)POLY_SMOOTH; |
| } |
| else if(basegfx::CONTINUITY_C2 == eCont) |
| { |
| mpImplPolygon->mpFlagAry[nStartPointIndex] = (sal_uInt8)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] = (sal_uInt8)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] = (sal_uInt8)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 |