1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_tools.hxx" 26 27 #define _SV_POLY_CXX 28 #include <osl/endian.h> 29 #include <tools/bigint.hxx> 30 #include <tools/debug.hxx> 31 #include <tools/stream.hxx> 32 #include <tools/vcompat.hxx> 33 #include <poly.h> 34 #include <tools/line.hxx> 35 #ifndef _VECTOR2D_H 36 #include <tools/vector2d.hxx> 37 #endif 38 #ifndef _POLY_HXX 39 #include <tools/poly.hxx> 40 #endif 41 #include <basegfx/polygon/b2dpolygon.hxx> 42 #include <basegfx/point/b2dpoint.hxx> 43 #include <basegfx/vector/b2dvector.hxx> 44 #include <basegfx/polygon/b2dpolygontools.hxx> 45 #include <basegfx/curve/b2dcubicbezier.hxx> 46 47 #include <vector> 48 #include <iterator> 49 #include <algorithm> 50 #include <cstring> 51 #include <limits.h> 52 #include <cmath> 53 54 55 // ======================================================================= 56 57 DBG_NAME( Polygon ) 58 59 // ----------------------------------------------------------------------- 60 61 #define EDGE_LEFT 1 62 #define EDGE_TOP 2 63 #define EDGE_RIGHT 4 64 #define EDGE_BOTTOM 8 65 #define EDGE_HORZ (EDGE_RIGHT | EDGE_LEFT) 66 #define EDGE_VERT (EDGE_TOP | EDGE_BOTTOM) 67 #define SMALL_DVALUE 0.0000001 68 #define FSQRT2 1.4142135623730950488016887242097 69 70 // ----------------------------------------------------------------------- 71 72 static ImplPolygonData aStaticImplPolygon = 73 { 74 NULL, NULL, 0, 0 75 }; 76 77 // ======================================================================= 78 79 ImplPolygon::ImplPolygon( sal_uInt16 nInitSize, sal_Bool bFlags ) 80 { 81 if ( nInitSize ) 82 { 83 mpPointAry = (Point*)new char[(sal_uIntPtr)nInitSize*sizeof(Point)]; 84 memset( mpPointAry, 0, (sal_uIntPtr)nInitSize*sizeof(Point) ); 85 } 86 else 87 mpPointAry = NULL; 88 89 if( bFlags ) 90 { 91 mpFlagAry = new sal_uInt8[ nInitSize ]; 92 memset( mpPointAry, 0, nInitSize ); 93 } 94 else 95 mpFlagAry = NULL; 96 97 mnRefCount = 1; 98 mnPoints = nInitSize; 99 } 100 101 // ----------------------------------------------------------------------- 102 103 ImplPolygon::ImplPolygon( const ImplPolygon& rImpPoly ) 104 { 105 if ( rImpPoly.mnPoints ) 106 { 107 mpPointAry = (Point*)new char[(sal_uIntPtr)rImpPoly.mnPoints*sizeof(Point)]; 108 memcpy( mpPointAry, rImpPoly.mpPointAry, (sal_uIntPtr)rImpPoly.mnPoints*sizeof(Point) ); 109 110 if( rImpPoly.mpFlagAry ) 111 { 112 mpFlagAry = new sal_uInt8[ rImpPoly.mnPoints ]; 113 memcpy( mpFlagAry, rImpPoly.mpFlagAry, rImpPoly.mnPoints ); 114 } 115 else 116 mpFlagAry = NULL; 117 } 118 else 119 { 120 mpPointAry = NULL; 121 mpFlagAry = NULL; 122 } 123 124 mnRefCount = 1; 125 mnPoints = rImpPoly.mnPoints; 126 } 127 128 // ----------------------------------------------------------------------- 129 130 ImplPolygon::ImplPolygon( sal_uInt16 nInitSize, const Point* pInitAry, const sal_uInt8* pInitFlags ) 131 { 132 if ( nInitSize ) 133 { 134 mpPointAry = (Point*)new char[(sal_uIntPtr)nInitSize*sizeof(Point)]; 135 memcpy( mpPointAry, pInitAry, (sal_uIntPtr)nInitSize*sizeof( Point ) ); 136 137 if( pInitFlags ) 138 { 139 mpFlagAry = new sal_uInt8[ nInitSize ]; 140 memcpy( mpFlagAry, pInitFlags, nInitSize ); 141 } 142 else 143 mpFlagAry = NULL; 144 } 145 else 146 { 147 mpPointAry = NULL; 148 mpFlagAry = NULL; 149 } 150 151 mnRefCount = 1; 152 mnPoints = nInitSize; 153 } 154 155 // ----------------------------------------------------------------------- 156 157 ImplPolygon::~ImplPolygon() 158 { 159 if ( mpPointAry ) 160 { 161 delete[] (char*) mpPointAry; 162 } 163 164 if( mpFlagAry ) 165 delete[] mpFlagAry; 166 } 167 168 // ----------------------------------------------------------------------- 169 170 void ImplPolygon::ImplSetSize( sal_uInt16 nNewSize, sal_Bool bResize ) 171 { 172 if( mnPoints == nNewSize ) 173 return; 174 175 Point* pNewAry; 176 177 if ( nNewSize ) 178 { 179 pNewAry = (Point*)new char[(sal_uIntPtr)nNewSize*sizeof(Point)]; 180 181 if ( bResize ) 182 { 183 // Alte Punkte kopieren 184 if ( mnPoints < nNewSize ) 185 { 186 // Neue Punkte mit 0 initialisieren 187 memset( pNewAry+mnPoints, 0, (sal_uIntPtr)(nNewSize-mnPoints)*sizeof(Point) ); 188 if ( mpPointAry ) 189 memcpy( pNewAry, mpPointAry, mnPoints*sizeof(Point) ); 190 } 191 else 192 { 193 if ( mpPointAry ) 194 memcpy( pNewAry, mpPointAry, (sal_uIntPtr)nNewSize*sizeof(Point) ); 195 } 196 } 197 } 198 else 199 pNewAry = NULL; 200 201 if ( mpPointAry ) 202 delete[] (char*) mpPointAry; 203 204 // ggf. FlagArray beruecksichtigen 205 if( mpFlagAry ) 206 { 207 sal_uInt8* pNewFlagAry; 208 209 if( nNewSize ) 210 { 211 pNewFlagAry = new sal_uInt8[ nNewSize ]; 212 213 if( bResize ) 214 { 215 // Alte Flags kopieren 216 if ( mnPoints < nNewSize ) 217 { 218 // Neue Punkte mit 0 initialisieren 219 memset( pNewFlagAry+mnPoints, 0, nNewSize-mnPoints ); 220 memcpy( pNewFlagAry, mpFlagAry, mnPoints ); 221 } 222 else 223 memcpy( pNewFlagAry, mpFlagAry, nNewSize ); 224 } 225 } 226 else 227 pNewFlagAry = NULL; 228 229 delete[] mpFlagAry; 230 mpFlagAry = pNewFlagAry; 231 } 232 233 mpPointAry = pNewAry; 234 mnPoints = nNewSize; 235 } 236 237 // ----------------------------------------------------------------------- 238 239 void ImplPolygon::ImplSplit( sal_uInt16 nPos, sal_uInt16 nSpace, ImplPolygon* pInitPoly ) 240 { 241 const sal_uIntPtr nSpaceSize = nSpace * sizeof( Point ); 242 243 //Can't fit this in :-(, throw ? 244 if (mnPoints + nSpace > USHRT_MAX) 245 return; 246 247 const sal_uInt16 nNewSize = mnPoints + nSpace; 248 249 if( nPos >= mnPoints ) 250 { 251 // Hinten anhaengen 252 nPos = mnPoints; 253 ImplSetSize( nNewSize, sal_True ); 254 255 if( pInitPoly ) 256 { 257 memcpy( mpPointAry + nPos, pInitPoly->mpPointAry, nSpaceSize ); 258 259 if( pInitPoly->mpFlagAry ) 260 memcpy( mpFlagAry + nPos, pInitPoly->mpFlagAry, nSpace ); 261 } 262 } 263 else 264 { 265 // PointArray ist in diesem Zweig immer vorhanden 266 const sal_uInt16 nSecPos = nPos + nSpace; 267 const sal_uInt16 nRest = mnPoints - nPos; 268 269 Point* pNewAry = (Point*) new char[ (sal_uIntPtr) nNewSize * sizeof( Point ) ]; 270 271 memcpy( pNewAry, mpPointAry, nPos * sizeof( Point ) ); 272 273 if( pInitPoly ) 274 memcpy( pNewAry + nPos, pInitPoly->mpPointAry, nSpaceSize ); 275 else 276 memset( pNewAry + nPos, 0, nSpaceSize ); 277 278 memcpy( pNewAry + nSecPos, mpPointAry + nPos, nRest * sizeof( Point ) ); 279 delete[] (char*) mpPointAry; 280 281 // ggf. FlagArray beruecksichtigen 282 if( mpFlagAry ) 283 { 284 sal_uInt8* pNewFlagAry = new sal_uInt8[ nNewSize ]; 285 286 memcpy( pNewFlagAry, mpFlagAry, nPos ); 287 288 if( pInitPoly && pInitPoly->mpFlagAry ) 289 memcpy( pNewFlagAry + nPos, pInitPoly->mpFlagAry, nSpace ); 290 else 291 memset( pNewFlagAry + nPos, 0, nSpace ); 292 293 memcpy( pNewFlagAry + nSecPos, mpFlagAry + nPos, nRest ); 294 delete[] mpFlagAry; 295 mpFlagAry = pNewFlagAry; 296 } 297 298 mpPointAry = pNewAry; 299 mnPoints = nNewSize; 300 } 301 } 302 303 // ----------------------------------------------------------------------- 304 305 void ImplPolygon::ImplRemove( sal_uInt16 nPos, sal_uInt16 nCount ) 306 { 307 const sal_uInt16 nRemoveCount = Min( (sal_uInt16) ( mnPoints - nPos ), (sal_uInt16) nCount ); 308 309 if( nRemoveCount ) 310 { 311 const sal_uInt16 nNewSize = mnPoints - nRemoveCount; 312 const sal_uInt16 nSecPos = nPos + nRemoveCount; 313 const sal_uInt16 nRest = mnPoints - nSecPos; 314 315 Point* pNewAry = (Point*) new char[ (sal_uIntPtr) nNewSize * sizeof( Point ) ]; 316 317 memcpy( pNewAry, mpPointAry, nPos * sizeof( Point ) ); 318 memcpy( pNewAry + nPos, mpPointAry + nSecPos, nRest * sizeof( Point ) ); 319 320 delete[] (char*) mpPointAry; 321 322 // ggf. FlagArray beruecksichtigen 323 if( mpFlagAry ) 324 { 325 sal_uInt8* pNewFlagAry = new sal_uInt8[ nNewSize ]; 326 327 memcpy( pNewFlagAry, mpFlagAry, nPos ); 328 memcpy( pNewFlagAry + nPos, mpFlagAry + nSecPos, nRest ); 329 delete[] mpFlagAry; 330 mpFlagAry = pNewFlagAry; 331 } 332 333 mpPointAry = pNewAry; 334 mnPoints = nNewSize; 335 } 336 } 337 338 // ----------------------------------------------------------------------- 339 340 void ImplPolygon::ImplCreateFlagArray() 341 { 342 if( !mpFlagAry ) 343 { 344 mpFlagAry = new sal_uInt8[ mnPoints ]; 345 memset( mpFlagAry, 0, mnPoints ); 346 } 347 } 348 349 // ======================================================================= 350 351 inline void Polygon::ImplMakeUnique() 352 { 353 // Falls noch andere Referenzen bestehen, dann kopieren 354 if ( mpImplPolygon->mnRefCount != 1 ) 355 { 356 if ( mpImplPolygon->mnRefCount ) 357 mpImplPolygon->mnRefCount--; 358 mpImplPolygon = new ImplPolygon( *mpImplPolygon ); 359 } 360 } 361 362 // ----------------------------------------------------------------------- 363 364 inline double ImplGetAngle( const Point& rCenter, const Point& rPt ) 365 { 366 const long nDX = rPt.X() - rCenter.X(); 367 return( atan2( -rPt.Y() + rCenter.Y(), ( ( nDX == 0L ) ? 0.000000001 : nDX ) ) ); 368 } 369 370 // ----------------------------------------------------------------------- 371 372 Polygon::Polygon() 373 { 374 DBG_CTOR( Polygon, NULL ); 375 mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); 376 } 377 378 // ----------------------------------------------------------------------- 379 380 Polygon::Polygon( sal_uInt16 nSize ) 381 { 382 DBG_CTOR( Polygon, NULL ); 383 384 if ( nSize ) 385 mpImplPolygon = new ImplPolygon( nSize ); 386 else 387 mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); 388 } 389 390 // ----------------------------------------------------------------------- 391 392 Polygon::Polygon( sal_uInt16 nPoints, const Point* pPtAry, const sal_uInt8* pFlagAry ) 393 { 394 DBG_CTOR( Polygon, NULL ); 395 396 if( nPoints ) 397 mpImplPolygon = new ImplPolygon( nPoints, pPtAry, pFlagAry ); 398 else 399 mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); 400 } 401 402 // ----------------------------------------------------------------------- 403 404 Polygon::Polygon( const Polygon& rPoly ) 405 { 406 DBG_CTOR( Polygon, NULL ); 407 DBG_CHKOBJ( &rPoly, Polygon, NULL ); 408 DBG_ASSERT( rPoly.mpImplPolygon->mnRefCount < 0xFFFFFFFE, "Polygon: RefCount overflow" ); 409 410 mpImplPolygon = rPoly.mpImplPolygon; 411 if ( mpImplPolygon->mnRefCount ) 412 mpImplPolygon->mnRefCount++; 413 } 414 415 // ----------------------------------------------------------------------- 416 417 Polygon::Polygon( const Rectangle& rRect ) 418 { 419 DBG_CTOR( Polygon, NULL ); 420 421 if ( rRect.IsEmpty() ) 422 mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); 423 else 424 { 425 mpImplPolygon = new ImplPolygon( 5 ); 426 mpImplPolygon->mpPointAry[0] = rRect.TopLeft(); 427 mpImplPolygon->mpPointAry[1] = rRect.TopRight(); 428 mpImplPolygon->mpPointAry[2] = rRect.BottomRight(); 429 mpImplPolygon->mpPointAry[3] = rRect.BottomLeft(); 430 mpImplPolygon->mpPointAry[4] = rRect.TopLeft(); 431 } 432 } 433 434 // ----------------------------------------------------------------------- 435 436 Polygon::Polygon( const Rectangle& rRect, sal_uIntPtr nHorzRound, sal_uIntPtr nVertRound ) 437 { 438 DBG_CTOR( Polygon, NULL ); 439 440 if ( rRect.IsEmpty() ) 441 mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); 442 else 443 { 444 Rectangle aRect( rRect ); 445 aRect.Justify(); // SJ: i9140 446 447 nHorzRound = Min( nHorzRound, (sal_uIntPtr) labs( aRect.GetWidth() >> 1 ) ); 448 nVertRound = Min( nVertRound, (sal_uIntPtr) labs( aRect.GetHeight() >> 1 ) ); 449 450 if( !nHorzRound && !nVertRound ) 451 { 452 mpImplPolygon = new ImplPolygon( 5 ); 453 mpImplPolygon->mpPointAry[0] = aRect.TopLeft(); 454 mpImplPolygon->mpPointAry[1] = aRect.TopRight(); 455 mpImplPolygon->mpPointAry[2] = aRect.BottomRight(); 456 mpImplPolygon->mpPointAry[3] = aRect.BottomLeft(); 457 mpImplPolygon->mpPointAry[4] = aRect.TopLeft(); 458 } 459 else 460 { 461 const Point aTL( aRect.Left() + nHorzRound, aRect.Top() + nVertRound ); 462 const Point aTR( aRect.Right() - nHorzRound, aRect.Top() + nVertRound ); 463 const Point aBR( aRect.Right() - nHorzRound, aRect.Bottom() - nVertRound ); 464 const Point aBL( aRect.Left() + nHorzRound, aRect.Bottom() - nVertRound ); 465 Polygon* pEllipsePoly = new Polygon( Point(), nHorzRound, nVertRound ); 466 sal_uInt16 i, nEnd, nSize4 = pEllipsePoly->GetSize() >> 2; 467 468 mpImplPolygon = new ImplPolygon( pEllipsePoly->GetSize() + 1 ); 469 470 const Point* pSrcAry = pEllipsePoly->GetConstPointAry(); 471 Point* pDstAry = mpImplPolygon->mpPointAry; 472 473 for( i = 0, nEnd = nSize4; i < nEnd; i++ ) 474 ( pDstAry[ i ] = pSrcAry[ i ] ) += aTR; 475 476 for( nEnd = nEnd + nSize4; i < nEnd; i++ ) 477 ( pDstAry[ i ] = pSrcAry[ i ] ) += aTL; 478 479 for( nEnd = nEnd + nSize4; i < nEnd; i++ ) 480 ( pDstAry[ i ] = pSrcAry[ i ] ) += aBL; 481 482 for( nEnd = nEnd + nSize4; i < nEnd; i++ ) 483 ( pDstAry[ i ] = pSrcAry[ i ] ) += aBR; 484 485 pDstAry[ nEnd ] = pDstAry[ 0 ]; 486 delete pEllipsePoly; 487 } 488 } 489 } 490 491 // ----------------------------------------------------------------------- 492 493 Polygon::Polygon( const Point& rCenter, long nRadX, long nRadY, sal_uInt16 nPoints ) 494 { 495 DBG_CTOR( Polygon, NULL ); 496 497 if( nRadX && nRadY ) 498 { 499 // Default berechnen (abhaengig von Groesse) 500 if( !nPoints ) 501 { 502 nPoints = (sal_uInt16) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - 503 sqrt( (double) labs( nRadX * nRadY ) ) ) ); 504 505 nPoints = (sal_uInt16) MinMax( nPoints, 32, 256 ); 506 507 if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 ) 508 nPoints >>= 1; 509 } 510 511 // Anzahl der Punkte auf durch 4 teilbare Zahl aufrunden 512 mpImplPolygon = new ImplPolygon( nPoints = (nPoints + 3) & ~3 ); 513 514 Point* pPt; 515 sal_uInt16 i; 516 sal_uInt16 nPoints2 = nPoints >> 1; 517 sal_uInt16 nPoints4 = nPoints >> 2; 518 double nAngle; 519 double nAngleStep = F_PI2 / ( nPoints4 - 1 ); 520 521 for( i=0, nAngle = 0.0; i < nPoints4; i++, nAngle += nAngleStep ) 522 { 523 long nX = FRound( nRadX * cos( nAngle ) ); 524 long nY = FRound( -nRadY * sin( nAngle ) ); 525 526 pPt = &(mpImplPolygon->mpPointAry[i]); 527 pPt->X() = nX + rCenter.X(); 528 pPt->Y() = nY + rCenter.Y(); 529 pPt = &(mpImplPolygon->mpPointAry[nPoints2-i-1]); 530 pPt->X() = -nX + rCenter.X(); 531 pPt->Y() = nY + rCenter.Y(); 532 pPt = &(mpImplPolygon->mpPointAry[i+nPoints2]); 533 pPt->X() = -nX + rCenter.X(); 534 pPt->Y() = -nY + rCenter.Y(); 535 pPt = &(mpImplPolygon->mpPointAry[nPoints-i-1]); 536 pPt->X() = nX + rCenter.X(); 537 pPt->Y() = -nY + rCenter.Y(); 538 } 539 } 540 else 541 mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); 542 } 543 544 // ----------------------------------------------------------------------- 545 546 Polygon::Polygon( const Rectangle& rBound, 547 const Point& rStart, const Point& rEnd, PolyStyle eStyle ) 548 { 549 DBG_CTOR( Polygon, NULL ); 550 551 const long nWidth = rBound.GetWidth(); 552 const long nHeight = rBound.GetHeight(); 553 554 if( ( nWidth > 1 ) && ( nHeight > 1 ) ) 555 { 556 const Point aCenter( rBound.Center() ); 557 const long nRadX = aCenter.X() - rBound.Left(); 558 const long nRadY = aCenter.Y() - rBound.Top(); 559 sal_uInt16 nPoints; 560 561 nPoints = (sal_uInt16) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - 562 sqrt( (double) labs( nRadX * nRadY ) ) ) ); 563 564 nPoints = (sal_uInt16) MinMax( nPoints, 32, 256 ); 565 566 if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 ) 567 nPoints >>= 1; 568 569 // Winkel berechnen 570 const double fRadX = nRadX; 571 const double fRadY = nRadY; 572 const double fCenterX = aCenter.X(); 573 const double fCenterY = aCenter.Y(); 574 double fStart = ImplGetAngle( aCenter, rStart ); 575 double fEnd = ImplGetAngle( aCenter, rEnd ); 576 double fDiff = fEnd - fStart; 577 double fStep; 578 sal_uInt16 nStart; 579 sal_uInt16 nEnd; 580 581 if( fDiff < 0. ) 582 fDiff += F_2PI; 583 584 // Punktanzahl proportional verkleinern ( fDiff / (2PI) ); 585 // ist eingentlich nur fuer einen Kreis richtig; wir 586 // machen es hier aber trotzdem 587 nPoints = Max( (sal_uInt16) ( ( fDiff * 0.1591549 ) * nPoints ), (sal_uInt16) 16 ); 588 fStep = fDiff / ( nPoints - 1 ); 589 590 if( POLY_PIE == eStyle ) 591 { 592 const Point aCenter2( FRound( fCenterX ), FRound( fCenterY ) ); 593 594 nStart = 1; 595 nEnd = nPoints + 1; 596 mpImplPolygon = new ImplPolygon( nPoints + 2 ); 597 mpImplPolygon->mpPointAry[ 0 ] = aCenter2; 598 mpImplPolygon->mpPointAry[ nEnd ] = aCenter2; 599 } 600 else 601 { 602 mpImplPolygon = new ImplPolygon( ( POLY_CHORD == eStyle ) ? ( nPoints + 1 ) : nPoints ); 603 nStart = 0; 604 nEnd = nPoints; 605 } 606 607 for(; nStart < nEnd; nStart++, fStart += fStep ) 608 { 609 Point& rPt = mpImplPolygon->mpPointAry[ nStart ]; 610 611 rPt.X() = FRound( fCenterX + fRadX * cos( fStart ) ); 612 rPt.Y() = FRound( fCenterY - fRadY * sin( fStart ) ); 613 } 614 615 if( POLY_CHORD == eStyle ) 616 mpImplPolygon->mpPointAry[ nPoints ] = mpImplPolygon->mpPointAry[ 0 ]; 617 } 618 else 619 mpImplPolygon = (ImplPolygon*) &aStaticImplPolygon; 620 } 621 622 // ----------------------------------------------------------------------- 623 624 Polygon::Polygon( const Point& rBezPt1, const Point& rCtrlPt1, 625 const Point& rBezPt2, const Point& rCtrlPt2, 626 sal_uInt16 nPoints ) 627 { 628 DBG_CTOR( Polygon, NULL ); 629 630 nPoints = ( 0 == nPoints ) ? 25 : ( ( nPoints < 2 ) ? 2 : nPoints ); 631 632 const double fInc = 1.0 / ( nPoints - 1 ); 633 double fK_1 = 0.0, fK1_1 = 1.0; 634 double fK_2, fK_3, fK1_2, fK1_3, fK12, fK21; 635 const double fX0 = rBezPt1.X(); 636 const double fY0 = rBezPt1.Y(); 637 const double fX1 = 3.0 * rCtrlPt1.X(); 638 const double fY1 = 3.0 * rCtrlPt1.Y(); 639 const double fX2 = 3.0 * rCtrlPt2.X();; 640 const double fY2 = 3.0 * rCtrlPt2.Y();; 641 const double fX3 = rBezPt2.X(); 642 const double fY3 = rBezPt2.Y(); 643 644 mpImplPolygon = new ImplPolygon( nPoints ); 645 646 for( sal_uInt16 i = 0; i < nPoints; i++, fK_1 += fInc, fK1_1 -= fInc ) 647 { 648 Point& rPt = mpImplPolygon->mpPointAry[ i ]; 649 650 fK_2 = fK_1, fK_3 = ( fK_2 *= fK_1 ), fK_3 *= fK_1; 651 fK1_2 = fK1_1, fK1_3 = ( fK1_2 *= fK1_1 ), fK1_3 *= fK1_1; 652 fK12 = fK_1 * fK1_2, fK21 = fK_2 * fK1_1; 653 654 rPt.X() = FRound( fK1_3 * fX0 + fK12 * fX1 + fK21 * fX2 + fK_3 * fX3 ); 655 rPt.Y() = FRound( fK1_3 * fY0 + fK12 * fY1 + fK21 * fY2 + fK_3 * fY3 ); 656 } 657 } 658 659 // ----------------------------------------------------------------------- 660 661 Polygon::~Polygon() 662 { 663 DBG_DTOR( Polygon, NULL ); 664 665 // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es 666 // die letzte Referenz ist, sonst Referenzcounter decrementieren 667 if ( mpImplPolygon->mnRefCount ) 668 { 669 if ( mpImplPolygon->mnRefCount > 1 ) 670 mpImplPolygon->mnRefCount--; 671 else 672 delete mpImplPolygon; 673 } 674 } 675 676 // ----------------------------------------------------------------------- 677 678 Point* Polygon::ImplGetPointAry() 679 { 680 DBG_CHKTHIS( Polygon, NULL ); 681 682 ImplMakeUnique(); 683 return (Point*)mpImplPolygon->mpPointAry; 684 } 685 686 // ----------------------------------------------------------------------- 687 688 sal_uInt8* Polygon::ImplGetFlagAry() 689 { 690 DBG_CHKTHIS( Polygon, NULL ); 691 692 ImplMakeUnique(); 693 mpImplPolygon->ImplCreateFlagArray(); 694 return mpImplPolygon->mpFlagAry; 695 } 696 697 // ----------------------------------------------------------------------- 698 699 const Point* Polygon::GetConstPointAry() const 700 { 701 DBG_CHKTHIS( Polygon, NULL ); 702 return (Point*)mpImplPolygon->mpPointAry; 703 } 704 705 // ----------------------------------------------------------------------- 706 707 const sal_uInt8* Polygon::GetConstFlagAry() const 708 { 709 DBG_CHKTHIS( Polygon, NULL ); 710 return mpImplPolygon->mpFlagAry; 711 } 712 713 // ----------------------------------------------------------------------- 714 715 void Polygon::SetPoint( const Point& rPt, sal_uInt16 nPos ) 716 { 717 DBG_CHKTHIS( Polygon, NULL ); 718 DBG_ASSERT( nPos < mpImplPolygon->mnPoints, 719 "Polygon::SetPoint(): nPos >= nPoints" ); 720 721 ImplMakeUnique(); 722 mpImplPolygon->mpPointAry[nPos] = rPt; 723 } 724 725 // ----------------------------------------------------------------------- 726 727 void Polygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags ) 728 { 729 DBG_CHKTHIS( Polygon, NULL ); 730 DBG_ASSERT( nPos < mpImplPolygon->mnPoints, 731 "Polygon::SetFlags(): nPos >= nPoints" ); 732 733 // we do only want to create the flag array if there 734 // is at least one flag different to POLY_NORMAL 735 if ( mpImplPolygon || ( eFlags != POLY_NORMAL ) ) 736 { 737 ImplMakeUnique(); 738 mpImplPolygon->ImplCreateFlagArray(); 739 mpImplPolygon->mpFlagAry[ nPos ] = (sal_uInt8) eFlags; 740 } 741 } 742 743 // ----------------------------------------------------------------------- 744 745 const Point& Polygon::GetPoint( sal_uInt16 nPos ) const 746 { 747 DBG_CHKTHIS( Polygon, NULL ); 748 DBG_ASSERT( nPos < mpImplPolygon->mnPoints, 749 "Polygon::GetPoint(): nPos >= nPoints" ); 750 751 return mpImplPolygon->mpPointAry[nPos]; 752 } 753 754 // ----------------------------------------------------------------------- 755 756 PolyFlags Polygon::GetFlags( sal_uInt16 nPos ) const 757 { 758 DBG_CHKTHIS( Polygon, NULL ); 759 DBG_ASSERT( nPos < mpImplPolygon->mnPoints, 760 "Polygon::GetFlags(): nPos >= nPoints" ); 761 return( mpImplPolygon->mpFlagAry ? 762 (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : 763 POLY_NORMAL ); 764 } 765 766 // ----------------------------------------------------------------------- 767 768 sal_Bool Polygon::HasFlags() const 769 { 770 return mpImplPolygon->mpFlagAry != NULL; 771 } 772 773 // ----------------------------------------------------------------------- 774 775 sal_Bool Polygon::IsControl(sal_uInt16 nPos) const 776 { 777 DBG_CHKTHIS( Polygon, NULL ); 778 DBG_ASSERT( nPos < mpImplPolygon->mnPoints, 779 "Polygon::GetFlags(): nPos >= nPoints" ); 780 PolyFlags eFlags = mpImplPolygon->mpFlagAry ? 781 (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : POLY_NORMAL; 782 783 return( POLY_CONTROL == eFlags ); 784 } 785 786 // ----------------------------------------------------------------------- 787 788 sal_Bool Polygon::IsSmooth(sal_uInt16 nPos) const 789 { 790 DBG_CHKTHIS( Polygon, NULL ); 791 DBG_ASSERT( nPos < mpImplPolygon->mnPoints, 792 "Polygon::GetFlags(): nPos >= nPoints" ); 793 PolyFlags eFlags = mpImplPolygon->mpFlagAry ? 794 (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : POLY_NORMAL; 795 796 return( ( POLY_SMOOTH == eFlags ) || ( POLY_SYMMTR == eFlags ) ); 797 } 798 799 // ----------------------------------------------------------------------- 800 801 sal_Bool Polygon::IsRect() const 802 { 803 sal_Bool bIsRect = sal_False; 804 if ( mpImplPolygon->mpFlagAry == NULL ) 805 { 806 if ( ( ( mpImplPolygon->mnPoints == 5 ) && ( mpImplPolygon->mpPointAry[ 0 ] == mpImplPolygon->mpPointAry[ 4 ] ) ) || 807 ( mpImplPolygon->mnPoints == 4 ) ) 808 { 809 if ( ( mpImplPolygon->mpPointAry[ 0 ].X() == mpImplPolygon->mpPointAry[ 3 ].X() ) && 810 ( mpImplPolygon->mpPointAry[ 0 ].Y() == mpImplPolygon->mpPointAry[ 1 ].Y() ) && 811 ( mpImplPolygon->mpPointAry[ 1 ].X() == mpImplPolygon->mpPointAry[ 2 ].X() ) && 812 ( mpImplPolygon->mpPointAry[ 2 ].Y() == mpImplPolygon->mpPointAry[ 3 ].Y() ) ) 813 bIsRect = sal_True; 814 } 815 } 816 return bIsRect; 817 } 818 819 // ----------------------------------------------------------------------- 820 821 void Polygon::SetSize( sal_uInt16 nNewSize ) 822 { 823 DBG_CHKTHIS( Polygon, NULL ); 824 825 if( nNewSize != mpImplPolygon->mnPoints ) 826 { 827 ImplMakeUnique(); 828 mpImplPolygon->ImplSetSize( nNewSize ); 829 } 830 } 831 832 // ----------------------------------------------------------------------- 833 834 sal_uInt16 Polygon::GetSize() const 835 { 836 DBG_CHKTHIS( Polygon, NULL ); 837 838 return mpImplPolygon->mnPoints; 839 } 840 841 // ----------------------------------------------------------------------- 842 843 void Polygon::Clear() 844 { 845 DBG_CHKTHIS( Polygon, NULL ); 846 847 if ( mpImplPolygon->mnRefCount ) 848 { 849 if ( mpImplPolygon->mnRefCount > 1 ) 850 mpImplPolygon->mnRefCount--; 851 else 852 delete mpImplPolygon; 853 } 854 855 mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); 856 } 857 858 // ----------------------------------------------------------------------- 859 860 double Polygon::CalcDistance( sal_uInt16 nP1, sal_uInt16 nP2 ) 861 { 862 DBG_ASSERT( nP1 < mpImplPolygon->mnPoints, 863 "Polygon::CalcDistance(): nPos1 >= nPoints" ); 864 DBG_ASSERT( nP2 < mpImplPolygon->mnPoints, 865 "Polygon::CalcDistance(): nPos2 >= nPoints" ); 866 867 const Point& rP1 = mpImplPolygon->mpPointAry[ nP1 ]; 868 const Point& rP2 = mpImplPolygon->mpPointAry[ nP2 ]; 869 const double fDx = rP2.X() - rP1.X(); 870 const double fDy = rP2.Y() - rP1.Y(); 871 872 return sqrt( fDx * fDx + fDy * fDy ); 873 } 874 875 // ----------------------------------------------------------------------- 876 877 void Polygon::Optimize( sal_uIntPtr nOptimizeFlags, const PolyOptimizeData* pData ) 878 { 879 DBG_CHKTHIS( Polygon, NULL ); 880 DBG_ASSERT( !mpImplPolygon->mpFlagAry, "Optimizing could fail with beziers!" ); 881 882 sal_uInt16 nSize = mpImplPolygon->mnPoints; 883 884 if( nOptimizeFlags && nSize ) 885 { 886 if( nOptimizeFlags & POLY_OPTIMIZE_EDGES ) 887 { 888 const Rectangle aBound( GetBoundRect() ); 889 const double fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5; 890 const sal_uInt16 nPercent = pData ? pData->GetPercentValue() : 50; 891 892 Optimize( POLY_OPTIMIZE_NO_SAME ); 893 ImplReduceEdges( *this, fArea, nPercent ); 894 } 895 else if( nOptimizeFlags & ( POLY_OPTIMIZE_REDUCE | POLY_OPTIMIZE_NO_SAME ) ) 896 { 897 Polygon aNewPoly; 898 const Point& rFirst = mpImplPolygon->mpPointAry[ 0 ]; 899 sal_uIntPtr nReduce; 900 901 if( nOptimizeFlags & ( POLY_OPTIMIZE_REDUCE ) ) 902 nReduce = pData ? pData->GetAbsValue() : 4UL; 903 else 904 nReduce = 0UL; 905 906 while( nSize && ( mpImplPolygon->mpPointAry[ nSize - 1 ] == rFirst ) ) 907 nSize--; 908 909 if( nSize > 1 ) 910 { 911 sal_uInt16 nLast = 0, nNewCount = 1; 912 913 aNewPoly.SetSize( nSize ); 914 aNewPoly[ 0 ] = rFirst; 915 916 for( sal_uInt16 i = 1; i < nSize; i++ ) 917 { 918 if( ( mpImplPolygon->mpPointAry[ i ] != mpImplPolygon->mpPointAry[ nLast ] ) && 919 ( !nReduce || ( nReduce < (sal_uIntPtr) FRound( CalcDistance( nLast, i ) ) ) ) ) 920 { 921 aNewPoly[ nNewCount++ ] = mpImplPolygon->mpPointAry[ nLast = i ]; 922 } 923 } 924 925 if( nNewCount == 1 ) 926 aNewPoly.Clear(); 927 else 928 aNewPoly.SetSize( nNewCount ); 929 } 930 931 *this = aNewPoly; 932 } 933 934 nSize = mpImplPolygon->mnPoints; 935 936 if( nSize > 1 ) 937 { 938 if( ( nOptimizeFlags & POLY_OPTIMIZE_CLOSE ) && 939 ( mpImplPolygon->mpPointAry[ 0 ] != mpImplPolygon->mpPointAry[ nSize - 1 ] ) ) 940 { 941 SetSize( mpImplPolygon->mnPoints + 1 ); 942 mpImplPolygon->mpPointAry[ mpImplPolygon->mnPoints - 1 ] = mpImplPolygon->mpPointAry[ 0 ]; 943 } 944 else if( ( nOptimizeFlags & POLY_OPTIMIZE_OPEN ) && 945 ( mpImplPolygon->mpPointAry[ 0 ] == mpImplPolygon->mpPointAry[ nSize - 1 ] ) ) 946 { 947 const Point& rFirst = mpImplPolygon->mpPointAry[ 0 ]; 948 949 while( nSize && ( mpImplPolygon->mpPointAry[ nSize - 1 ] == rFirst ) ) 950 nSize--; 951 952 SetSize( nSize ); 953 } 954 } 955 } 956 } 957 958 // ======================================================================= 959 960 /* Recursively subdivide cubic bezier curve via deCasteljau. 961 962 @param rPointIter 963 Output iterator, where the subdivided polylines are written to. 964 965 @param d 966 Squared difference of curve to a straight line 967 968 @param P* 969 Exactly four points, interpreted as support and control points of 970 a cubic bezier curve. Must be in device coordinates, since stop 971 criterion is based on the following assumption: the device has a 972 finite resolution, it is thus sufficient to stop subdivision if the 973 curve does not deviate more than one pixel from a straight line. 974 975 */ 976 static void ImplAdaptiveSubdivide( ::std::back_insert_iterator< ::std::vector< Point > >& rPointIter, 977 const double old_d2, 978 int recursionDepth, 979 const double d2, 980 const double P1x, const double P1y, 981 const double P2x, const double P2y, 982 const double P3x, const double P3y, 983 const double P4x, const double P4y ) 984 { 985 // Hard limit on recursion depth, empiric number. 986 enum {maxRecursionDepth=128}; 987 988 // Perform bezier flatness test (lecture notes from R. Schaback, 989 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000) 990 // 991 // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)|| 992 // 0<=j<=n 993 // 994 // What is calculated here is an upper bound to the distance from 995 // a line through b_0 and b_3 (P1 and P4 in our notation) and the 996 // curve. We can drop 0 and n from the running indices, since the 997 // argument of max becomes zero for those cases. 998 const double fJ1x( P2x - P1x - 1.0/3.0*(P4x - P1x) ); 999 const double fJ1y( P2y - P1y - 1.0/3.0*(P4y - P1y) ); 1000 const double fJ2x( P3x - P1x - 2.0/3.0*(P4x - P1x) ); 1001 const double fJ2y( P3y - P1y - 2.0/3.0*(P4y - P1y) ); 1002 const double distance2( ::std::max( fJ1x*fJ1x + fJ1y*fJ1y, 1003 fJ2x*fJ2x + fJ2y*fJ2y) ); 1004 1005 // stop if error measure does not improve anymore. This is a 1006 // safety guard against floating point inaccuracies. 1007 // stop at recursion level 128. This is a safety guard against 1008 // floating point inaccuracies. 1009 // stop if distance from line is guaranteed to be bounded by d 1010 if( old_d2 > d2 && 1011 recursionDepth < maxRecursionDepth && 1012 distance2 >= d2 ) 1013 { 1014 // deCasteljau bezier arc, split at t=0.5 1015 // Foley/vanDam, p. 508 1016 const double L1x( P1x ), L1y( P1y ); 1017 const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 ); 1018 const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 ); 1019 const double L3x( (L2x + Hx)*0.5 ), L3y( (L2y + Hy)*0.5 ); 1020 const double R4x( P4x ), R4y( P4y ); 1021 const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 ); 1022 const double R2x( (Hx + R3x)*0.5 ), R2y( (Hy + R3y)*0.5 ); 1023 const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 ); 1024 const double L4x( R1x ), L4y( R1y ); 1025 1026 // subdivide further 1027 ++recursionDepth; 1028 ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y); 1029 ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y); 1030 } 1031 else 1032 { 1033 // requested resolution reached. 1034 // Add end points to output iterator. 1035 // order is preserved, since this is so to say depth first traversal. 1036 *rPointIter++ = Point( FRound(P1x), FRound(P1y) ); 1037 } 1038 } 1039 1040 // ======================================================================= 1041 1042 void Polygon::AdaptiveSubdivide( Polygon& rResult, const double d ) const 1043 { 1044 if( !mpImplPolygon->mpFlagAry ) 1045 { 1046 rResult = *this; 1047 } 1048 else 1049 { 1050 sal_uInt16 i; 1051 sal_uInt16 nPts( GetSize() ); 1052 ::std::vector< Point > aPoints; 1053 aPoints.reserve( nPts ); 1054 ::std::back_insert_iterator< ::std::vector< Point > > aPointIter( aPoints ); 1055 1056 for(i=0; i<nPts;) 1057 { 1058 if( ( i + 3 ) < nPts ) 1059 { 1060 sal_uInt8 P1( mpImplPolygon->mpFlagAry[ i ] ); 1061 sal_uInt8 P4( mpImplPolygon->mpFlagAry[ i + 3 ] ); 1062 1063 if( ( POLY_NORMAL == P1 || POLY_SMOOTH == P1 || POLY_SYMMTR == P1 ) && 1064 ( POLY_CONTROL == mpImplPolygon->mpFlagAry[ i + 1 ] ) && 1065 ( POLY_CONTROL == mpImplPolygon->mpFlagAry[ i + 2 ] ) && 1066 ( POLY_NORMAL == P4 || POLY_SMOOTH == P4 || POLY_SYMMTR == P4 ) ) 1067 { 1068 ImplAdaptiveSubdivide( aPointIter, d*d+1.0, 0, d*d, 1069 mpImplPolygon->mpPointAry[ i ].X(), mpImplPolygon->mpPointAry[ i ].Y(), 1070 mpImplPolygon->mpPointAry[ i+1 ].X(), mpImplPolygon->mpPointAry[ i+1 ].Y(), 1071 mpImplPolygon->mpPointAry[ i+2 ].X(), mpImplPolygon->mpPointAry[ i+2 ].Y(), 1072 mpImplPolygon->mpPointAry[ i+3 ].X(), mpImplPolygon->mpPointAry[ i+3 ].Y() ); 1073 i += 3; 1074 continue; 1075 } 1076 } 1077 1078 *aPointIter++ = mpImplPolygon->mpPointAry[ i++ ]; 1079 } 1080 1081 // fill result polygon 1082 rResult = Polygon( (sal_uInt16)aPoints.size() ); // ensure sufficient size for copy 1083 ::std::copy(aPoints.begin(), aPoints.end(), rResult.mpImplPolygon->mpPointAry); 1084 } 1085 } 1086 1087 // ----------------------------------------------------------------------- 1088 1089 void Polygon::GetIntersection( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const 1090 { 1091 const PolyPolygon aTmp( *this ); 1092 aTmp.GetIntersection( rPolyPoly, rResult ); 1093 } 1094 1095 // ----------------------------------------------------------------------- 1096 1097 void Polygon::GetUnion( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const 1098 { 1099 const PolyPolygon aTmp( *this ); 1100 aTmp.GetUnion( rPolyPoly, rResult ); 1101 } 1102 1103 // ----------------------------------------------------------------------- 1104 1105 void Polygon::GetDifference( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const 1106 { 1107 const PolyPolygon aTmp( *this ); 1108 aTmp.GetDifference( rPolyPoly, rResult ); 1109 } 1110 1111 // ----------------------------------------------------------------------- 1112 1113 void Polygon::GetXOR( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const 1114 { 1115 const PolyPolygon aTmp( *this ); 1116 aTmp.GetXOR( rPolyPoly, rResult ); 1117 } 1118 1119 // ----------------------------------------------------------------------- 1120 1121 void Polygon::ImplReduceEdges( Polygon& rPoly, const double& rArea, sal_uInt16 nPercent ) 1122 { 1123 const double fBound = 2000.0 * ( 100 - nPercent ) * 0.01; 1124 sal_uInt16 nNumNoChange = 0, nNumRuns = 0; 1125 1126 while( nNumNoChange < 2 ) 1127 { 1128 sal_uInt16 nPntCnt = rPoly.GetSize(), nNewPos = 0; 1129 Polygon aNewPoly( nPntCnt ); 1130 sal_Bool bChangeInThisRun = sal_False; 1131 1132 for( sal_uInt16 n = 0; n < nPntCnt; n++ ) 1133 { 1134 sal_Bool bDeletePoint = sal_False; 1135 1136 if( ( n + nNumRuns ) % 2 ) 1137 { 1138 sal_uInt16 nIndPrev = !n ? nPntCnt - 1 : n - 1; 1139 sal_uInt16 nIndPrevPrev = !nIndPrev ? nPntCnt - 1 : nIndPrev - 1; 1140 sal_uInt16 nIndNext = ( n == nPntCnt-1 ) ? 0 : n + 1; 1141 sal_uInt16 nIndNextNext = ( nIndNext == nPntCnt - 1 ) ? 0 : nIndNext + 1; 1142 Vector2D aVec1( rPoly[ nIndPrev ] ); aVec1 -= rPoly[ nIndPrevPrev ]; 1143 Vector2D aVec2( rPoly[ n ] ); aVec2 -= rPoly[ nIndPrev ]; 1144 Vector2D aVec3( rPoly[ nIndNext ] ); aVec3 -= rPoly[ n ]; 1145 Vector2D aVec4( rPoly[ nIndNextNext ] ); aVec4 -= rPoly[ nIndNext ]; 1146 double fDist1 = aVec1.GetLength(), fDist2 = aVec2.GetLength(); 1147 double fDist3 = aVec3.GetLength(), fDist4 = aVec4.GetLength(); 1148 double fTurnB = aVec2.Normalize().Scalar( aVec3.Normalize() ); 1149 1150 if( fabs( fTurnB ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnB ) > ( 1.0 - SMALL_DVALUE ) ) 1151 bDeletePoint = sal_True; 1152 else 1153 { 1154 Vector2D aVecB( rPoly[ nIndNext ] ); 1155 double fDistB = ( aVecB -= rPoly[ nIndPrev ] ).GetLength(); 1156 double fLenWithB = fDist2 + fDist3; 1157 double fLenFact = ( fDistB != 0.0 ) ? fLenWithB / fDistB : 1.0; 1158 double fTurnPrev = aVec1.Normalize().Scalar( aVec2 ); 1159 double fTurnNext = aVec3.Scalar( aVec4.Normalize() ); 1160 double fGradPrev, fGradB, fGradNext; 1161 1162 if( fabs( fTurnPrev ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnPrev ) > ( 1.0 - SMALL_DVALUE ) ) 1163 fGradPrev = 0.0; 1164 else 1165 fGradPrev = acos( fTurnPrev ) / ( aVec1.IsNegative( aVec2 ) ? -F_PI180 : F_PI180 ); 1166 1167 fGradB = acos( fTurnB ) / ( aVec2.IsNegative( aVec3 ) ? -F_PI180 : F_PI180 ); 1168 1169 if( fabs( fTurnNext ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnNext ) > ( 1.0 - SMALL_DVALUE ) ) 1170 fGradNext = 0.0; 1171 else 1172 fGradNext = acos( fTurnNext ) / ( aVec3.IsNegative( aVec4 ) ? -F_PI180 : F_PI180 ); 1173 1174 if( ( fGradPrev > 0.0 && fGradB < 0.0 && fGradNext > 0.0 ) || 1175 ( fGradPrev < 0.0 && fGradB > 0.0 && fGradNext < 0.0 ) ) 1176 { 1177 if( ( fLenFact < ( FSQRT2 + SMALL_DVALUE ) ) && 1178 ( ( ( fDist1 + fDist4 ) / ( fDist2 + fDist3 ) ) * 2000.0 ) > fBound ) 1179 { 1180 bDeletePoint = sal_True; 1181 } 1182 } 1183 else 1184 { 1185 double fRelLen = 1.0 - sqrt( fDistB / rArea ); 1186 1187 if( fRelLen < 0.0 ) 1188 fRelLen = 0.0; 1189 else if( fRelLen > 1.0 ) 1190 fRelLen = 1.0; 1191 1192 if( ( (sal_uInt32) ( ( ( fLenFact - 1.0 ) * 1000000.0 ) + 0.5 ) < fBound ) && 1193 ( fabs( fGradB ) <= ( fRelLen * fBound * 0.01 ) ) ) 1194 { 1195 bDeletePoint = sal_True; 1196 } 1197 } 1198 } 1199 } 1200 1201 if( !bDeletePoint ) 1202 aNewPoly[ nNewPos++ ] = rPoly[ n ]; 1203 else 1204 bChangeInThisRun = sal_True; 1205 } 1206 1207 if( bChangeInThisRun && nNewPos ) 1208 { 1209 aNewPoly.SetSize( nNewPos ); 1210 rPoly = aNewPoly; 1211 nNumNoChange = 0; 1212 } 1213 else 1214 nNumNoChange++; 1215 1216 nNumRuns++; 1217 } 1218 } 1219 1220 // ----------------------------------------------------------------------- 1221 1222 void Polygon::Move( long nHorzMove, long nVertMove ) 1223 { 1224 DBG_CHKTHIS( Polygon, NULL ); 1225 1226 // Diese Abfrage sollte man fuer die DrawEngine durchfuehren 1227 if ( !nHorzMove && !nVertMove ) 1228 return; 1229 1230 ImplMakeUnique(); 1231 1232 // Punkte verschieben 1233 sal_uInt16 nCount = mpImplPolygon->mnPoints; 1234 for ( sal_uInt16 i = 0; i < nCount; i++ ) 1235 { 1236 Point* pPt = &(mpImplPolygon->mpPointAry[i]); 1237 pPt->X() += nHorzMove; 1238 pPt->Y() += nVertMove; 1239 } 1240 } 1241 1242 // ----------------------------------------------------------------------- 1243 1244 void Polygon::Translate(const Point& rTrans) 1245 { 1246 DBG_CHKTHIS( Polygon, NULL ); 1247 ImplMakeUnique(); 1248 1249 for ( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) 1250 mpImplPolygon->mpPointAry[ i ] += rTrans; 1251 } 1252 1253 // ----------------------------------------------------------------------- 1254 1255 void Polygon::Scale( double fScaleX, double fScaleY ) 1256 { 1257 DBG_CHKTHIS( Polygon, NULL ); 1258 ImplMakeUnique(); 1259 1260 for ( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) 1261 { 1262 Point& rPnt = mpImplPolygon->mpPointAry[i]; 1263 rPnt.X() = (long) ( fScaleX * rPnt.X() ); 1264 rPnt.Y() = (long) ( fScaleY * rPnt.Y() ); 1265 } 1266 } 1267 1268 // ----------------------------------------------------------------------- 1269 1270 void Polygon::Rotate( const Point& rCenter, sal_uInt16 nAngle10 ) 1271 { 1272 DBG_CHKTHIS( Polygon, NULL ); 1273 nAngle10 %= 3600; 1274 1275 if( nAngle10 ) 1276 { 1277 const double fAngle = F_PI1800 * nAngle10; 1278 Rotate( rCenter, sin( fAngle ), cos( fAngle ) ); 1279 } 1280 } 1281 1282 // ----------------------------------------------------------------------- 1283 1284 void Polygon::Rotate( const Point& rCenter, double fSin, double fCos ) 1285 { 1286 DBG_CHKTHIS( Polygon, NULL ); 1287 ImplMakeUnique(); 1288 1289 long nX, nY; 1290 long nCenterX = rCenter.X(); 1291 long nCenterY = rCenter.Y(); 1292 1293 for( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) 1294 { 1295 Point& rPt = mpImplPolygon->mpPointAry[ i ]; 1296 1297 nX = rPt.X() - nCenterX; 1298 nY = rPt.Y() - nCenterY; 1299 rPt.X() = (long) FRound( fCos * nX + fSin * nY ) + nCenterX; 1300 rPt.Y() = -(long) FRound( fSin * nX - fCos * nY ) + nCenterY; 1301 } 1302 } 1303 1304 // ----------------------------------------------------------------------- 1305 1306 void Polygon::SlantX( long nYRef, double fSin, double fCos ) 1307 { 1308 DBG_CHKTHIS( Polygon, NULL ); 1309 ImplMakeUnique(); 1310 1311 for( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) 1312 { 1313 Point& rPnt = mpImplPolygon->mpPointAry[ i ]; 1314 const long nDy = rPnt.Y() - nYRef; 1315 1316 rPnt.X() += (long)( fSin * nDy ); 1317 rPnt.Y() = nYRef + (long)( fCos * nDy ); 1318 } 1319 } 1320 1321 // ----------------------------------------------------------------------- 1322 1323 void Polygon::SlantY( long nXRef, double fSin, double fCos ) 1324 { 1325 DBG_CHKTHIS( Polygon, NULL ); 1326 ImplMakeUnique(); 1327 1328 for( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) 1329 { 1330 Point& rPnt = mpImplPolygon->mpPointAry[ i ]; 1331 const long nDx = rPnt.X() - nXRef; 1332 1333 rPnt.X() = nXRef + (long)( fCos * nDx ); 1334 rPnt.Y() -= (long)( fSin * nDx ); 1335 } 1336 } 1337 1338 // ----------------------------------------------------------------------- 1339 1340 void Polygon::Distort( const Rectangle& rRefRect, const Polygon& rDistortedRect ) 1341 { 1342 DBG_CHKTHIS( Polygon, NULL ); 1343 ImplMakeUnique(); 1344 1345 long Xr, Wr, X1, X2, X3, X4; 1346 long Yr, Hr, Y1, Y2, Y3, Y4; 1347 double fTx, fTy, fUx, fUy; 1348 1349 Xr = rRefRect.Left(); 1350 Yr = rRefRect.Top(); 1351 Wr = rRefRect.GetWidth(); 1352 Hr = rRefRect.GetHeight(); 1353 1354 if( Wr && Hr ) 1355 { 1356 DBG_ASSERT( rDistortedRect.mpImplPolygon->mnPoints >= 4, "Distort rect too small!" ); 1357 1358 X1 = rDistortedRect[0].X(); 1359 Y1 = rDistortedRect[0].Y(); 1360 X2 = rDistortedRect[1].X(); 1361 Y2 = rDistortedRect[1].Y(); 1362 X3 = rDistortedRect[3].X(); 1363 Y3 = rDistortedRect[3].Y(); 1364 X4 = rDistortedRect[2].X(); 1365 Y4 = rDistortedRect[2].Y(); 1366 1367 for( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) 1368 { 1369 Point& rPnt = mpImplPolygon->mpPointAry[ i ]; 1370 1371 fTx = (double)( rPnt.X() - Xr) / Wr; 1372 fTy = (double)( rPnt.Y() - Yr) / Hr; 1373 fUx = 1.0 - fTx; 1374 fUy = 1.0 - fTy; 1375 1376 rPnt.X() = (long) ( fUy * (fUx * X1 + fTx * X2) + fTy * (fUx * X3 + fTx * X4) ); 1377 rPnt.Y() = (long) ( fUx * (fUy * Y1 + fTy * Y3) + fTx * (fUy * Y2 + fTy * Y4) ); 1378 } 1379 } 1380 } 1381 1382 // ----------------------------------------------------------------------- 1383 1384 class ImplPointFilter 1385 { 1386 public: 1387 virtual void LastPoint() = 0; 1388 virtual void Input( const Point& rPoint ) = 0; 1389 }; 1390 1391 class ImplPolygonPointFilter : public ImplPointFilter 1392 { 1393 public: 1394 ImplPolygon* mpPoly; // Nicht loeschen, wird dem Polygon zugewiesen 1395 sal_uInt16 mnSize; 1396 1397 ImplPolygonPointFilter( sal_uInt16 nDestSize ) : 1398 mnSize( 0 ) 1399 { 1400 mpPoly = new ImplPolygon( nDestSize ); 1401 } 1402 1403 virtual void LastPoint(); 1404 virtual void Input( const Point& rPoint ); 1405 }; 1406 1407 void ImplPolygonPointFilter::Input( const Point& rPoint ) 1408 { 1409 if ( !mnSize || (rPoint != mpPoly->mpPointAry[mnSize-1]) ) 1410 { 1411 mnSize++; 1412 if ( mnSize > mpPoly->mnPoints ) 1413 mpPoly->ImplSetSize( mnSize ); 1414 mpPoly->mpPointAry[mnSize-1] = rPoint; 1415 } 1416 } 1417 1418 void ImplPolygonPointFilter::LastPoint() 1419 { 1420 if ( mnSize < mpPoly->mnPoints ) 1421 mpPoly->ImplSetSize( mnSize ); 1422 }; 1423 1424 class ImplEdgePointFilter : public ImplPointFilter 1425 { 1426 Point maFirstPoint; 1427 Point maLastPoint; 1428 ImplPointFilter& mrNextFilter; 1429 const long mnLow; 1430 const long mnHigh; 1431 const int mnEdge; 1432 int mnLastOutside; 1433 sal_Bool mbFirst; 1434 1435 public: 1436 ImplEdgePointFilter( int nEdge, long nLow, long nHigh, 1437 ImplPointFilter& rNextFilter ) : 1438 mrNextFilter( rNextFilter ), 1439 mnLow( nLow ), 1440 mnHigh( nHigh ), 1441 mnEdge( nEdge ), 1442 mbFirst( sal_True ) 1443 { 1444 } 1445 1446 Point EdgeSection( const Point& rPoint, int nEdge ) const; 1447 int VisibleSide( const Point& rPoint ) const; 1448 int IsPolygon() const 1449 { return maFirstPoint == maLastPoint; } 1450 1451 virtual void Input( const Point& rPoint ); 1452 virtual void LastPoint(); 1453 }; 1454 1455 inline int ImplEdgePointFilter::VisibleSide( const Point& rPoint ) const 1456 { 1457 if ( mnEdge & EDGE_HORZ ) 1458 { 1459 return rPoint.X() < mnLow ? EDGE_LEFT : 1460 rPoint.X() > mnHigh ? EDGE_RIGHT : 0; 1461 } 1462 else 1463 { 1464 return rPoint.Y() < mnLow ? EDGE_TOP : 1465 rPoint.Y() > mnHigh ? EDGE_BOTTOM : 0; 1466 } 1467 } 1468 1469 Point ImplEdgePointFilter::EdgeSection( const Point& rPoint, int nEdge ) const 1470 { 1471 long lx = maLastPoint.X(); 1472 long ly = maLastPoint.Y(); 1473 long md = rPoint.X() - lx; 1474 long mn = rPoint.Y() - ly; 1475 long nNewX; 1476 long nNewY; 1477 1478 if ( nEdge & EDGE_VERT ) 1479 { 1480 nNewY = (nEdge == EDGE_TOP) ? mnLow : mnHigh; 1481 long dy = nNewY - ly; 1482 if ( !md ) 1483 nNewX = lx; 1484 else if ( (LONG_MAX / Abs(md)) >= Abs(dy) ) 1485 nNewX = (dy * md) / mn + lx; 1486 else 1487 { 1488 BigInt ady = dy; 1489 ady *= md; 1490 if( ady.IsNeg() ) 1491 if( mn < 0 ) 1492 ady += mn/2; 1493 else 1494 ady -= (mn-1)/2; 1495 else 1496 if( mn < 0 ) 1497 ady -= (mn+1)/2; 1498 else 1499 ady += mn/2; 1500 ady /= mn; 1501 nNewX = (long)ady + lx; 1502 } 1503 } 1504 else 1505 { 1506 nNewX = (nEdge == EDGE_LEFT) ? mnLow : mnHigh; 1507 long dx = nNewX - lx; 1508 if ( !mn ) 1509 nNewY = ly; 1510 else if ( (LONG_MAX / Abs(mn)) >= Abs(dx) ) 1511 nNewY = (dx * mn) / md + ly; 1512 else 1513 { 1514 BigInt adx = dx; 1515 adx *= mn; 1516 if( adx.IsNeg() ) 1517 if( md < 0 ) 1518 adx += md/2; 1519 else 1520 adx -= (md-1)/2; 1521 else 1522 if( md < 0 ) 1523 adx -= (md+1)/2; 1524 else 1525 adx += md/2; 1526 adx /= md; 1527 nNewY = (long)adx + ly; 1528 } 1529 } 1530 1531 return Point( nNewX, nNewY ); 1532 } 1533 1534 void ImplEdgePointFilter::Input( const Point& rPoint ) 1535 { 1536 int nOutside = VisibleSide( rPoint ); 1537 1538 if ( mbFirst ) 1539 { 1540 maFirstPoint = rPoint; 1541 mbFirst = sal_False; 1542 if ( !nOutside ) 1543 mrNextFilter.Input( rPoint ); 1544 } 1545 else if ( rPoint == maLastPoint ) 1546 return; 1547 else if ( !nOutside ) 1548 { 1549 if ( mnLastOutside ) 1550 mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) ); 1551 mrNextFilter.Input( rPoint ); 1552 } 1553 else if ( !mnLastOutside ) 1554 mrNextFilter.Input( EdgeSection( rPoint, nOutside ) ); 1555 else if ( nOutside != mnLastOutside ) 1556 { 1557 mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) ); 1558 mrNextFilter.Input( EdgeSection( rPoint, nOutside ) ); 1559 } 1560 1561 maLastPoint = rPoint; 1562 mnLastOutside = nOutside; 1563 } 1564 1565 void ImplEdgePointFilter::LastPoint() 1566 { 1567 if ( !mbFirst ) 1568 { 1569 int nOutside = VisibleSide( maFirstPoint ); 1570 1571 if ( nOutside != mnLastOutside ) 1572 Input( maFirstPoint ); 1573 mrNextFilter.LastPoint(); 1574 } 1575 } 1576 1577 // ----------------------------------------------------------------------- 1578 1579 void Polygon::Clip( const Rectangle& rRect, sal_Bool bPolygon ) 1580 { 1581 // #105251# Justify rect befor edge filtering 1582 Rectangle aJustifiedRect( rRect ); 1583 aJustifiedRect.Justify(); 1584 1585 sal_uInt16 nSourceSize = mpImplPolygon->mnPoints; 1586 ImplPolygonPointFilter aPolygon( nSourceSize ); 1587 ImplEdgePointFilter aHorzFilter( EDGE_HORZ, aJustifiedRect.Left(), aJustifiedRect.Right(), 1588 aPolygon ); 1589 ImplEdgePointFilter aVertFilter( EDGE_VERT, aJustifiedRect.Top(), aJustifiedRect.Bottom(), 1590 aHorzFilter ); 1591 1592 for ( sal_uInt16 i = 0; i < nSourceSize; i++ ) 1593 aVertFilter.Input( mpImplPolygon->mpPointAry[i] ); 1594 if ( bPolygon || aVertFilter.IsPolygon() ) 1595 aVertFilter.LastPoint(); 1596 else 1597 aPolygon.LastPoint(); 1598 1599 // Alte ImpPolygon-Daten loeschen und die vom ImpPolygonPointFilter 1600 // zuweisen 1601 if ( mpImplPolygon->mnRefCount ) 1602 { 1603 if ( mpImplPolygon->mnRefCount > 1 ) 1604 mpImplPolygon->mnRefCount--; 1605 else 1606 delete mpImplPolygon; 1607 } 1608 mpImplPolygon = aPolygon.mpPoly; 1609 } 1610 1611 // ----------------------------------------------------------------------- 1612 1613 Rectangle Polygon::GetBoundRect() const 1614 { 1615 DBG_CHKTHIS( Polygon, NULL ); 1616 // Removing the assert. Bezier curves have the attribute that each single 1617 // curve segment defined by four points can not exit the four-point polygon 1618 // defined by that points. This allows to say that the curve segment can also 1619 // never leave the Range of it's defining points. 1620 // The result is that Polygon::GetBoundRect() may not create the minimal 1621 // BoundRect of the Polygon (to get that, use basegfx::B2DPolygon classes), 1622 // but will always create a valid BoundRect, at least as long as this method 1623 // 'blindly' travels over all points, including control points. 1624 // 1625 // DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetBoundRect could fail with beziers!" ); 1626 1627 sal_uInt16 nCount = mpImplPolygon->mnPoints; 1628 if( ! nCount ) 1629 return Rectangle(); 1630 1631 long nXMin, nXMax, nYMin, nYMax; 1632 1633 const Point* pPt = &(mpImplPolygon->mpPointAry[0]); 1634 nXMin = nXMax = pPt->X(); 1635 nYMin = nYMax = pPt->Y(); 1636 1637 for ( sal_uInt16 i = 0; i < nCount; i++ ) 1638 { 1639 pPt = &(mpImplPolygon->mpPointAry[i]); 1640 1641 if ( pPt->X() < nXMin ) 1642 nXMin = pPt->X(); 1643 if ( pPt->X() > nXMax ) 1644 nXMax = pPt->X(); 1645 if ( pPt->Y() < nYMin ) 1646 nYMin = pPt->Y(); 1647 if ( pPt->Y() > nYMax ) 1648 nYMax = pPt->Y(); 1649 } 1650 1651 return Rectangle( nXMin, nYMin, nXMax, nYMax ); 1652 } 1653 1654 // ----------------------------------------------------------------------- 1655 1656 double Polygon::GetArea() const 1657 { 1658 const double fArea = GetSignedArea(); 1659 return( ( fArea < 0.0 ) ? -fArea : fArea ); 1660 } 1661 1662 // ----------------------------------------------------------------------- 1663 1664 double Polygon::GetSignedArea() const 1665 { 1666 DBG_CHKTHIS( Polygon, NULL ); 1667 DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetArea could fail with beziers!" ); 1668 1669 double fArea = 0.0; 1670 1671 if( mpImplPolygon->mnPoints > 2 ) 1672 { 1673 const sal_uInt16 nCount1 = mpImplPolygon->mnPoints - 1; 1674 1675 for( sal_uInt16 i = 0; i < nCount1; ) 1676 { 1677 const Point& rPt = mpImplPolygon->mpPointAry[ i ]; 1678 const Point& rPt1 = mpImplPolygon->mpPointAry[ ++i ]; 1679 fArea += ( rPt.X() - rPt1.X() ) * ( rPt.Y() + rPt1.Y() ); 1680 } 1681 1682 const Point& rPt = mpImplPolygon->mpPointAry[ nCount1 ]; 1683 const Point& rPt0 = mpImplPolygon->mpPointAry[ 0 ]; 1684 fArea += ( rPt.X() - rPt0.X() ) * ( rPt.Y() + rPt0.Y() ); 1685 } 1686 1687 return fArea; 1688 } 1689 1690 // ----------------------------------------------------------------------- 1691 1692 sal_Bool Polygon::IsInside( const Point& rPoint ) const 1693 { 1694 DBG_CHKTHIS( Polygon, NULL ); 1695 DBG_ASSERT( !mpImplPolygon->mpFlagAry, "IsInside could fail with beziers!" ); 1696 1697 const Rectangle aBound( GetBoundRect() ); 1698 const Line aLine( rPoint, Point( aBound.Right() + 100L, rPoint.Y() ) ); 1699 sal_uInt16 nCount = mpImplPolygon->mnPoints; 1700 sal_uInt16 nPCounter = 0; 1701 1702 if ( ( nCount > 2 ) && aBound.IsInside( rPoint ) ) 1703 { 1704 Point aPt1( mpImplPolygon->mpPointAry[ 0 ] ); 1705 Point aIntersection; 1706 Point aLastIntersection; 1707 1708 while ( ( aPt1 == mpImplPolygon->mpPointAry[ nCount - 1 ] ) && ( nCount > 3 ) ) 1709 nCount--; 1710 1711 for ( sal_uInt16 i = 1; i <= nCount; i++ ) 1712 { 1713 const Point& rPt2 = mpImplPolygon->mpPointAry[ ( i < nCount ) ? i : 0 ]; 1714 1715 if ( aLine.Intersection( Line( aPt1, rPt2 ), aIntersection ) ) 1716 { 1717 // Hiermit verhindern wir das Einfuegen von 1718 // doppelten Intersections, die gleich hintereinander folgen 1719 if ( nPCounter ) 1720 { 1721 if ( aIntersection != aLastIntersection ) 1722 { 1723 aLastIntersection = aIntersection; 1724 nPCounter++; 1725 } 1726 } 1727 else 1728 { 1729 aLastIntersection = aIntersection; 1730 nPCounter++; 1731 } 1732 } 1733 1734 aPt1 = rPt2; 1735 } 1736 } 1737 1738 // innerhalb, wenn die Anzahl der Schnittpunkte ungerade ist 1739 return ( ( nPCounter & 1 ) == 1 ); 1740 } 1741 1742 // ----------------------------------------------------------------------- 1743 1744 sal_Bool Polygon::IsRightOrientated() const 1745 { 1746 DBG_CHKTHIS( Polygon, NULL ); 1747 return GetSignedArea() >= 0.0; 1748 } 1749 1750 // ----------------------------------------------------------------------- 1751 1752 void Polygon::Insert( sal_uInt16 nPos, const Point& rPt, PolyFlags eFlags ) 1753 { 1754 DBG_CHKTHIS( Polygon, NULL ); 1755 ImplMakeUnique(); 1756 1757 if( nPos >= mpImplPolygon->mnPoints ) 1758 nPos = mpImplPolygon->mnPoints; 1759 1760 mpImplPolygon->ImplSplit( nPos, 1 ); 1761 mpImplPolygon->mpPointAry[ nPos ] = rPt; 1762 1763 if( POLY_NORMAL != eFlags ) 1764 { 1765 mpImplPolygon->ImplCreateFlagArray(); 1766 mpImplPolygon->mpFlagAry[ nPos ] = (sal_uInt8) eFlags; 1767 } 1768 } 1769 1770 // ----------------------------------------------------------------------- 1771 1772 void Polygon::Insert( sal_uInt16 nPos, const Polygon& rPoly ) 1773 { 1774 DBG_CHKTHIS( Polygon, NULL ); 1775 const sal_uInt16 nInsertCount = rPoly.mpImplPolygon->mnPoints; 1776 1777 if( nInsertCount ) 1778 { 1779 ImplMakeUnique(); 1780 1781 if( nPos >= mpImplPolygon->mnPoints ) 1782 nPos = mpImplPolygon->mnPoints; 1783 1784 if( rPoly.mpImplPolygon->mpFlagAry ) 1785 mpImplPolygon->ImplCreateFlagArray(); 1786 1787 mpImplPolygon->ImplSplit( nPos, nInsertCount, rPoly.mpImplPolygon ); 1788 } 1789 } 1790 1791 // ----------------------------------------------------------------------- 1792 1793 void Polygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount ) 1794 { 1795 DBG_CHKTHIS( Polygon, NULL ); 1796 if( nCount && ( nPos < mpImplPolygon->mnPoints ) ) 1797 { 1798 ImplMakeUnique(); 1799 mpImplPolygon->ImplRemove( nPos, nCount ); 1800 } 1801 } 1802 1803 // ----------------------------------------------------------------------- 1804 1805 Point& Polygon::operator[]( sal_uInt16 nPos ) 1806 { 1807 DBG_CHKTHIS( Polygon, NULL ); 1808 DBG_ASSERT( nPos < mpImplPolygon->mnPoints, "Polygon::[]: nPos >= nPoints" ); 1809 1810 ImplMakeUnique(); 1811 return mpImplPolygon->mpPointAry[nPos]; 1812 } 1813 1814 // ----------------------------------------------------------------------- 1815 1816 Polygon& Polygon::operator=( const Polygon& rPoly ) 1817 { 1818 DBG_CHKTHIS( Polygon, NULL ); 1819 DBG_CHKOBJ( &rPoly, Polygon, NULL ); 1820 DBG_ASSERT( rPoly.mpImplPolygon->mnRefCount < 0xFFFFFFFE, "Polygon: RefCount overflow" ); 1821 1822 // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann 1823 // RefCount == 0 fuer statische Objekte 1824 if ( rPoly.mpImplPolygon->mnRefCount ) 1825 rPoly.mpImplPolygon->mnRefCount++; 1826 1827 // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es 1828 // die letzte Referenz ist, sonst Referenzcounter decrementieren 1829 if ( mpImplPolygon->mnRefCount ) 1830 { 1831 if ( mpImplPolygon->mnRefCount > 1 ) 1832 mpImplPolygon->mnRefCount--; 1833 else 1834 delete mpImplPolygon; 1835 } 1836 1837 mpImplPolygon = rPoly.mpImplPolygon; 1838 return *this; 1839 } 1840 1841 // ----------------------------------------------------------------------- 1842 1843 sal_Bool Polygon::operator==( const Polygon& rPoly ) const 1844 { 1845 DBG_CHKTHIS( Polygon, NULL ); 1846 DBG_CHKOBJ( &rPoly, Polygon, NULL ); 1847 1848 if ( (rPoly.mpImplPolygon == mpImplPolygon) ) 1849 return sal_True; 1850 else 1851 return sal_False; 1852 } 1853 1854 // ----------------------------------------------------------------------- 1855 1856 sal_Bool Polygon::IsEqual( const Polygon& rPoly ) const 1857 { 1858 sal_Bool bIsEqual = sal_True;; 1859 sal_uInt16 i; 1860 if ( GetSize() != rPoly.GetSize() ) 1861 bIsEqual = sal_False; 1862 else 1863 { 1864 for ( i = 0; i < GetSize(); i++ ) 1865 { 1866 if ( ( GetPoint( i ) != rPoly.GetPoint( i ) ) || 1867 ( GetFlags( i ) != rPoly.GetFlags( i ) ) ) 1868 { 1869 bIsEqual = sal_False; 1870 break; 1871 } 1872 } 1873 } 1874 return bIsEqual; 1875 } 1876 1877 // ----------------------------------------------------------------------- 1878 1879 SvStream& operator>>( SvStream& rIStream, Polygon& rPoly ) 1880 { 1881 DBG_CHKOBJ( &rPoly, Polygon, NULL ); 1882 DBG_ASSERTWARNING( rIStream.GetVersion(), "Polygon::>> - Solar-Version not set on rIStream" ); 1883 1884 sal_uInt16 i; 1885 sal_uInt16 nStart; 1886 sal_uInt16 nCurPoints; 1887 sal_uInt16 nPoints; 1888 unsigned char bShort; 1889 short nShortX; 1890 short nShortY; 1891 long nLongX; 1892 long nLongY; 1893 1894 // Anzahl der Punkte einlesen und Array erzeugen 1895 rIStream >> nPoints; 1896 if ( rPoly.mpImplPolygon->mnRefCount != 1 ) 1897 { 1898 if ( rPoly.mpImplPolygon->mnRefCount ) 1899 rPoly.mpImplPolygon->mnRefCount--; 1900 rPoly.mpImplPolygon = new ImplPolygon( nPoints ); 1901 } 1902 else 1903 rPoly.mpImplPolygon->ImplSetSize( nPoints, sal_False ); 1904 1905 // Je nach CompressMode das Polygon einlesen 1906 if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) 1907 { 1908 i = 0; 1909 while ( i < nPoints ) 1910 { 1911 rIStream >> bShort >> nCurPoints; 1912 1913 if ( bShort ) 1914 { 1915 for ( nStart = i; i < nStart+nCurPoints; i++ ) 1916 { 1917 rIStream >> nShortX >> nShortY; 1918 rPoly.mpImplPolygon->mpPointAry[i].X() = nShortX; 1919 rPoly.mpImplPolygon->mpPointAry[i].Y() = nShortY; 1920 } 1921 } 1922 else 1923 { 1924 for ( nStart = i; i < nStart+nCurPoints; i++ ) 1925 { 1926 rIStream >> nLongX >> nLongY; 1927 rPoly.mpImplPolygon->mpPointAry[i].X() = nLongX; 1928 rPoly.mpImplPolygon->mpPointAry[i].Y() = nLongY; 1929 } 1930 } 1931 } 1932 } 1933 else 1934 { 1935 // Feststellen, ob ueber die Operatoren geschrieben werden muss 1936 #if (SAL_TYPES_SIZEOFLONG) != 4 1937 if ( 1 ) 1938 #else 1939 #ifdef OSL_BIGENDIAN 1940 if ( rIStream.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN ) 1941 #else 1942 if ( rIStream.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN ) 1943 #endif 1944 #endif 1945 { 1946 for( i = 0; i < nPoints; i++ ) 1947 { 1948 rIStream >> rPoly.mpImplPolygon->mpPointAry[i].X() 1949 >> rPoly.mpImplPolygon->mpPointAry[i].Y(); 1950 } 1951 } 1952 else 1953 rIStream.Read( rPoly.mpImplPolygon->mpPointAry, nPoints*sizeof(Point) ); 1954 } 1955 1956 return rIStream; 1957 } 1958 1959 // ----------------------------------------------------------------------- 1960 1961 SvStream& operator<<( SvStream& rOStream, const Polygon& rPoly ) 1962 { 1963 DBG_CHKOBJ( &rPoly, Polygon, NULL ); 1964 DBG_ASSERTWARNING( rOStream.GetVersion(), "Polygon::<< - Solar-Version not set on rOStream" ); 1965 1966 unsigned char bShort; 1967 unsigned char bCurShort; 1968 sal_uInt16 nStart; 1969 sal_uInt16 i; 1970 sal_uInt16 nPoints = rPoly.GetSize(); 1971 1972 // Anzahl der Punkte rausschreiben 1973 rOStream << nPoints; 1974 1975 // Je nach CompressMode das Polygon rausschreiben 1976 if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) 1977 { 1978 i = 0; 1979 while ( i < nPoints ) 1980 { 1981 nStart = i; 1982 1983 // Feststellen, welcher Typ geschrieben werden soll 1984 if ( ((rPoly.mpImplPolygon->mpPointAry[nStart].X() >= SHRT_MIN) && 1985 (rPoly.mpImplPolygon->mpPointAry[nStart].X() <= SHRT_MAX)) && 1986 ((rPoly.mpImplPolygon->mpPointAry[nStart].Y() >= SHRT_MIN) && 1987 (rPoly.mpImplPolygon->mpPointAry[nStart].Y() <= SHRT_MAX)) ) 1988 bShort = sal_True; 1989 else 1990 bShort = sal_False; 1991 while ( i < nPoints ) 1992 { 1993 // Feststellen, welcher Typ geschrieben werden soll 1994 if ( ((rPoly.mpImplPolygon->mpPointAry[nStart].X() >= SHRT_MIN) && 1995 (rPoly.mpImplPolygon->mpPointAry[nStart].X() <= SHRT_MAX)) && 1996 ((rPoly.mpImplPolygon->mpPointAry[nStart].Y() >= SHRT_MIN) && 1997 (rPoly.mpImplPolygon->mpPointAry[nStart].Y() <= SHRT_MAX)) ) 1998 bCurShort = sal_True; 1999 else 2000 bCurShort = sal_False; 2001 2002 // Wenn sich die Werte in einen anderen Bereich begeben, 2003 // muessen wir neu rausschreiben 2004 if ( bCurShort != bShort ) 2005 { 2006 bShort = bCurShort; 2007 break; 2008 } 2009 2010 i++; 2011 } 2012 2013 rOStream << bShort << (sal_uInt16)(i-nStart); 2014 2015 if ( bShort ) 2016 { 2017 for( ; nStart < i; nStart++ ) 2018 { 2019 rOStream << (short)rPoly.mpImplPolygon->mpPointAry[nStart].X() 2020 << (short)rPoly.mpImplPolygon->mpPointAry[nStart].Y(); 2021 } 2022 } 2023 else 2024 { 2025 for( ; nStart < i; nStart++ ) 2026 { 2027 rOStream << rPoly.mpImplPolygon->mpPointAry[nStart].X() 2028 << rPoly.mpImplPolygon->mpPointAry[nStart].Y(); 2029 } 2030 } 2031 } 2032 } 2033 else 2034 { 2035 // Feststellen, ob ueber die Operatoren geschrieben werden muss 2036 #if (SAL_TYPES_SIZEOFLONG) != 4 2037 if ( 1 ) 2038 #else 2039 #ifdef OSL_BIGENDIAN 2040 if ( rOStream.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN ) 2041 #else 2042 if ( rOStream.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN ) 2043 #endif 2044 #endif 2045 { 2046 for( i = 0; i < nPoints; i++ ) 2047 { 2048 rOStream << rPoly.mpImplPolygon->mpPointAry[i].X() 2049 << rPoly.mpImplPolygon->mpPointAry[i].Y(); 2050 } 2051 } 2052 else 2053 { 2054 if ( nPoints ) 2055 rOStream.Write( rPoly.mpImplPolygon->mpPointAry, nPoints*sizeof(Point) ); 2056 } 2057 } 2058 2059 return rOStream; 2060 } 2061 2062 // ----------------------------------------------------------------------- 2063 2064 void Polygon::ImplRead( SvStream& rIStream ) 2065 { 2066 sal_uInt8 bHasPolyFlags; 2067 2068 rIStream >> *this 2069 >> bHasPolyFlags; 2070 2071 if ( bHasPolyFlags ) 2072 { 2073 mpImplPolygon->mpFlagAry = new sal_uInt8[ mpImplPolygon->mnPoints ]; 2074 rIStream.Read( mpImplPolygon->mpFlagAry, mpImplPolygon->mnPoints ); 2075 } 2076 } 2077 2078 // ----------------------------------------------------------------------- 2079 2080 void Polygon::Read( SvStream& rIStream ) 2081 { 2082 VersionCompat aCompat( rIStream, STREAM_READ ); 2083 2084 ImplRead( rIStream ); 2085 } 2086 2087 // ----------------------------------------------------------------------- 2088 2089 void Polygon::ImplWrite( SvStream& rOStream ) const 2090 { 2091 sal_uInt8 bHasPolyFlags = mpImplPolygon->mpFlagAry != NULL; 2092 rOStream << *this 2093 << bHasPolyFlags; 2094 2095 if ( bHasPolyFlags ) 2096 rOStream.Write( mpImplPolygon->mpFlagAry, mpImplPolygon->mnPoints ); 2097 } 2098 2099 // ----------------------------------------------------------------------- 2100 2101 void Polygon::Write( SvStream& rOStream ) const 2102 { 2103 VersionCompat aCompat( rOStream, STREAM_WRITE, 1 ); 2104 2105 ImplWrite( rOStream ); 2106 } 2107 2108 // ----------------------------------------------------------------------- 2109 // #i74631# numerical correction method for B2DPolygon 2110 void impCorrectContinuity(basegfx::B2DPolygon& roPolygon, sal_uInt32 nIndex, sal_uInt8 nCFlag) 2111 { 2112 const sal_uInt32 nPointCount(roPolygon.count()); 2113 OSL_ENSURE(nIndex < nPointCount, "impCorrectContinuity: index access out of range (!)"); 2114 2115 if(nIndex < nPointCount && (POLY_SMOOTH == nCFlag || POLY_SYMMTR == nCFlag)) 2116 { 2117 if(roPolygon.isPrevControlPointUsed(nIndex) && roPolygon.isNextControlPointUsed(nIndex)) 2118 { 2119 const basegfx::B2DPoint aPoint(roPolygon.getB2DPoint(nIndex)); 2120 2121 if(POLY_SMOOTH == nCFlag) 2122 { 2123 // C1: apply inverse direction of prev to next, keep length of next 2124 const basegfx::B2DVector aOriginalNext(roPolygon.getNextControlPoint(nIndex) - aPoint); 2125 basegfx::B2DVector aNewNext(aPoint - roPolygon.getPrevControlPoint(nIndex)); 2126 2127 aNewNext.setLength(aOriginalNext.getLength()); 2128 roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + aNewNext)); 2129 } 2130 else // POLY_SYMMTR 2131 { 2132 // C2: apply inverse control point to next 2133 roPolygon.setNextControlPoint(nIndex, (2.0 * aPoint) - roPolygon.getPrevControlPoint(nIndex)); 2134 } 2135 } 2136 } 2137 } 2138 2139 // ----------------------------------------------------------------------- 2140 // convert to basegfx::B2DPolygon and return 2141 basegfx::B2DPolygon Polygon::getB2DPolygon() const 2142 { 2143 basegfx::B2DPolygon aRetval; 2144 const sal_uInt16 nCount(mpImplPolygon->mnPoints); 2145 2146 if(nCount) 2147 { 2148 if(mpImplPolygon->mpFlagAry) 2149 { 2150 // handling for curves. Add start point 2151 const Point aStartPoint(mpImplPolygon->mpPointAry[0]); 2152 sal_uInt8 nPointFlag(mpImplPolygon->mpFlagAry[0]); 2153 aRetval.append(basegfx::B2DPoint(aStartPoint.X(), aStartPoint.Y())); 2154 Point aControlA, aControlB; 2155 2156 for(sal_uInt16 a(1); a < nCount;) 2157 { 2158 bool bControlA(false); 2159 bool bControlB(false); 2160 2161 if(POLY_CONTROL == mpImplPolygon->mpFlagAry[a]) 2162 { 2163 aControlA = mpImplPolygon->mpPointAry[a++]; 2164 bControlA = true; 2165 } 2166 2167 if(a < nCount && POLY_CONTROL == mpImplPolygon->mpFlagAry[a]) 2168 { 2169 aControlB = mpImplPolygon->mpPointAry[a++]; 2170 bControlB = true; 2171 } 2172 2173 // assert invalid polygons 2174 OSL_ENSURE(bControlA == bControlB, "Polygon::getB2DPolygon: Invalid source polygon (!)"); 2175 2176 if(a < nCount) 2177 { 2178 const Point aEndPoint(mpImplPolygon->mpPointAry[a]); 2179 2180 if(bControlA) 2181 { 2182 // bezier edge, add 2183 aRetval.appendBezierSegment( 2184 basegfx::B2DPoint(aControlA.X(), aControlA.Y()), 2185 basegfx::B2DPoint(aControlB.X(), aControlB.Y()), 2186 basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y())); 2187 2188 impCorrectContinuity(aRetval, aRetval.count() - 2, nPointFlag); 2189 } 2190 else 2191 { 2192 // no bezier edge, add end point 2193 aRetval.append(basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y())); 2194 } 2195 2196 nPointFlag = mpImplPolygon->mpFlagAry[a++]; 2197 } 2198 } 2199 2200 // if exist, remove double first/last points, set closed and correct control points 2201 basegfx::tools::checkClosed(aRetval); 2202 2203 if(aRetval.isClosed()) 2204 { 2205 // closeWithGeometryChange did really close, so last point(s) were removed. 2206 // Correct the continuity in the changed point 2207 impCorrectContinuity(aRetval, 0, mpImplPolygon->mpFlagAry[0]); 2208 } 2209 } 2210 else 2211 { 2212 // extra handling for non-curves (most-used case) for speedup 2213 for(sal_uInt16 a(0); a < nCount; a++) 2214 { 2215 // get point and add 2216 const Point aPoint(mpImplPolygon->mpPointAry[a]); 2217 aRetval.append(basegfx::B2DPoint(aPoint.X(), aPoint.Y())); 2218 } 2219 2220 // set closed flag 2221 basegfx::tools::checkClosed(aRetval); 2222 } 2223 } 2224 2225 return aRetval; 2226 } 2227 2228 // ----------------------------------------------------------------------- 2229 // constructor to convert from basegfx::B2DPolygon 2230 // #i76891# Needed to change from adding all control points (even for unused 2231 // edges) and creating a fixed-size Polygon in the first run to creating the 2232 // minimal Polygon. This requires a temporary Point- and Flag-Array for curves 2233 // and a memcopy at ImplPolygon creation, but contains no zero-controlpoints 2234 // for straight edges. 2235 Polygon::Polygon(const basegfx::B2DPolygon& rPolygon) 2236 : mpImplPolygon(0) 2237 { 2238 DBG_CTOR( Polygon, NULL ); 2239 2240 const bool bCurve(rPolygon.areControlPointsUsed()); 2241 const bool bClosed(rPolygon.isClosed()); 2242 sal_uInt32 nB2DLocalCount(rPolygon.count()); 2243 2244 if(bCurve) 2245 { 2246 // #127979# Reduce source point count hard to the limit of the tools Polygon 2247 if(nB2DLocalCount > ((0x0000ffff / 3L) - 1L)) 2248 { 2249 DBG_ERROR("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"); 2250 nB2DLocalCount = ((0x0000ffff / 3L) - 1L); 2251 } 2252 2253 // calculate target point count 2254 const sal_uInt32 nLoopCount(bClosed ? nB2DLocalCount : (nB2DLocalCount ? nB2DLocalCount - 1L : 0L )); 2255 2256 if(nLoopCount) 2257 { 2258 // calculate maximum array size and allocate; prepare insert index 2259 const sal_uInt32 nMaxTargetCount((nLoopCount * 3) + 1); 2260 mpImplPolygon = new ImplPolygon(static_cast< sal_uInt16 >(nMaxTargetCount), true); 2261 2262 // prepare insert index and current point 2263 sal_uInt32 nArrayInsert(0); 2264 basegfx::B2DCubicBezier aBezier; 2265 aBezier.setStartPoint(rPolygon.getB2DPoint(0)); 2266 2267 for(sal_uInt32 a(0L); a < nLoopCount; a++) 2268 { 2269 // add current point (always) and remember StartPointIndex for evtl. later corrections 2270 const Point aStartPoint(FRound(aBezier.getStartPoint().getX()), FRound(aBezier.getStartPoint().getY())); 2271 const sal_uInt32 nStartPointIndex(nArrayInsert); 2272 mpImplPolygon->mpPointAry[nStartPointIndex] = aStartPoint; 2273 mpImplPolygon->mpFlagAry[nStartPointIndex] = (sal_uInt8)POLY_NORMAL; 2274 nArrayInsert++; 2275 2276 // prepare next segment 2277 const sal_uInt32 nNextIndex((a + 1) % nB2DLocalCount); 2278 aBezier.setEndPoint(rPolygon.getB2DPoint(nNextIndex)); 2279 aBezier.setControlPointA(rPolygon.getNextControlPoint(a)); 2280 aBezier.setControlPointB(rPolygon.getPrevControlPoint(nNextIndex)); 2281 2282 if(aBezier.isBezier()) 2283 { 2284 // if one is used, add always two control points due to the old schema 2285 mpImplPolygon->mpPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointA().getX()), FRound(aBezier.getControlPointA().getY())); 2286 mpImplPolygon->mpFlagAry[nArrayInsert] = (sal_uInt8)POLY_CONTROL; 2287 nArrayInsert++; 2288 2289 mpImplPolygon->mpPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointB().getX()), FRound(aBezier.getControlPointB().getY())); 2290 mpImplPolygon->mpFlagAry[nArrayInsert] = (sal_uInt8)POLY_CONTROL; 2291 nArrayInsert++; 2292 } 2293 2294 // test continuity with previous control point to set flag value 2295 if(aBezier.getControlPointA() != aBezier.getStartPoint() && (bClosed || a)) 2296 { 2297 const basegfx::B2VectorContinuity eCont(rPolygon.getContinuityInPoint(a)); 2298 2299 if(basegfx::CONTINUITY_C1 == eCont) 2300 { 2301 mpImplPolygon->mpFlagAry[nStartPointIndex] = (sal_uInt8)POLY_SMOOTH; 2302 } 2303 else if(basegfx::CONTINUITY_C2 == eCont) 2304 { 2305 mpImplPolygon->mpFlagAry[nStartPointIndex] = (sal_uInt8)POLY_SYMMTR; 2306 } 2307 } 2308 2309 // prepare next polygon step 2310 aBezier.setStartPoint(aBezier.getEndPoint()); 2311 } 2312 2313 if(bClosed) 2314 { 2315 // add first point again as closing point due to old definition 2316 mpImplPolygon->mpPointAry[nArrayInsert] = mpImplPolygon->mpPointAry[0]; 2317 mpImplPolygon->mpFlagAry[nArrayInsert] = (sal_uInt8)POLY_NORMAL; 2318 nArrayInsert++; 2319 } 2320 else 2321 { 2322 // add last point as closing point 2323 const basegfx::B2DPoint aClosingPoint(rPolygon.getB2DPoint(nB2DLocalCount - 1L)); 2324 const Point aEnd(FRound(aClosingPoint.getX()), FRound(aClosingPoint.getY())); 2325 mpImplPolygon->mpPointAry[nArrayInsert] = aEnd; 2326 mpImplPolygon->mpFlagAry[nArrayInsert] = (sal_uInt8)POLY_NORMAL; 2327 nArrayInsert++; 2328 } 2329 2330 DBG_ASSERT(nArrayInsert <= nMaxTargetCount, "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)"); 2331 2332 if(nArrayInsert != nMaxTargetCount) 2333 { 2334 mpImplPolygon->ImplSetSize(static_cast< sal_uInt16 >(nArrayInsert), true); 2335 } 2336 } 2337 } 2338 else 2339 { 2340 // #127979# Reduce source point count hard to the limit of the tools Polygon 2341 if(nB2DLocalCount > (0x0000ffff - 1L)) 2342 { 2343 DBG_ERROR("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"); 2344 nB2DLocalCount = (0x0000ffff - 1L); 2345 } 2346 2347 if(nB2DLocalCount) 2348 { 2349 // point list creation 2350 const sal_uInt32 nTargetCount(nB2DLocalCount + (bClosed ? 1L : 0L)); 2351 mpImplPolygon = new ImplPolygon( static_cast< sal_uInt16 >(nTargetCount) ); 2352 sal_uInt16 nIndex(0); 2353 2354 for(sal_uInt32 a(0L); a < nB2DLocalCount; a++) 2355 { 2356 basegfx::B2DPoint aB2DPoint(rPolygon.getB2DPoint(a)); 2357 Point aPoint(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY())); 2358 mpImplPolygon->mpPointAry[nIndex++] = aPoint; 2359 } 2360 2361 if(bClosed) 2362 { 2363 // add first point as closing point 2364 mpImplPolygon->mpPointAry[nIndex] = mpImplPolygon->mpPointAry[0]; 2365 } 2366 } 2367 } 2368 2369 if(!mpImplPolygon) 2370 { 2371 // no content yet, create empty polygon 2372 mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); 2373 } 2374 } 2375 2376 // eof 2377