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_svx.hxx" 26 27 #include <svx/svdtrans.hxx> 28 #include <math.h> 29 #include <svx/xpoly.hxx> 30 31 #include <vcl/virdev.hxx> 32 #include <tools/bigint.hxx> 33 #include <tools/debug.hxx> 34 #include <unotools/syslocale.hxx> 35 36 //////////////////////////////////////////////////////////////////////////////////////////////////// 37 38 void MoveXPoly(XPolygon& rPoly, const Size& S) 39 { 40 rPoly.Move(S.Width(),S.Height()); 41 } 42 43 void MoveXPoly(XPolyPolygon& rPoly, const Size& S) 44 { 45 rPoly.Move(S.Width(),S.Height()); 46 } 47 48 //////////////////////////////////////////////////////////////////////////////////////////////////// 49 50 void ResizeRect(Rectangle& rRect, const Point& rRef, const Fraction& rxFact, const Fraction& ryFact, FASTBOOL bNoJustify) 51 { 52 Fraction xFact(rxFact); 53 Fraction yFact(ryFact); 54 //long nHgt=rRect.Bottom()-rRect.Top(); 55 56 { 57 if (xFact.GetDenominator()==0) { 58 long nWdt=rRect.Right()-rRect.Left(); 59 if (xFact.GetNumerator()>=0) { // DivZero abfangen 60 xFact=Fraction(xFact.GetNumerator(),1); 61 if (nWdt==0) rRect.Right()++; 62 } else { 63 xFact=Fraction(xFact.GetNumerator(),-1); 64 if (nWdt==0) rRect.Left()--; 65 } 66 } 67 rRect.Left() =rRef.X()+Round(((double)(rRect.Left() -rRef.X())*xFact.GetNumerator())/xFact.GetDenominator()); 68 rRect.Right() =rRef.X()+Round(((double)(rRect.Right() -rRef.X())*xFact.GetNumerator())/xFact.GetDenominator()); 69 } 70 { 71 if (yFact.GetDenominator()==0) { 72 long nHgt=rRect.Bottom()-rRect.Top(); 73 if (yFact.GetNumerator()>=0) { // DivZero abfangen 74 yFact=Fraction(yFact.GetNumerator(),1); 75 if (nHgt==0) rRect.Bottom()++; 76 } else { 77 yFact=Fraction(yFact.GetNumerator(),-1); 78 if (nHgt==0) rRect.Top()--; 79 } 80 81 yFact=Fraction(yFact.GetNumerator(),1); // DivZero abfangen 82 } 83 rRect.Top() =rRef.Y()+Round(((double)(rRect.Top() -rRef.Y())*yFact.GetNumerator())/yFact.GetDenominator()); 84 rRect.Bottom()=rRef.Y()+Round(((double)(rRect.Bottom()-rRef.Y())*yFact.GetNumerator())/yFact.GetDenominator()); 85 } 86 if (!bNoJustify) rRect.Justify(); 87 } 88 89 90 void ResizePoly(Polygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact) 91 { 92 sal_uInt16 nAnz=rPoly.GetSize(); 93 for (sal_uInt16 i=0; i<nAnz; i++) { 94 ResizePoint(rPoly[i],rRef,xFact,yFact); 95 } 96 } 97 98 void ResizeXPoly(XPolygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact) 99 { 100 sal_uInt16 nAnz=rPoly.GetPointCount(); 101 for (sal_uInt16 i=0; i<nAnz; i++) { 102 ResizePoint(rPoly[i],rRef,xFact,yFact); 103 } 104 } 105 106 void ResizePoly(PolyPolygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact) 107 { 108 sal_uInt16 nAnz=rPoly.Count(); 109 for (sal_uInt16 i=0; i<nAnz; i++) { 110 ResizePoly(rPoly[i],rRef,xFact,yFact); 111 } 112 } 113 114 void ResizeXPoly(XPolyPolygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact) 115 { 116 sal_uInt16 nAnz=rPoly.Count(); 117 for (sal_uInt16 i=0; i<nAnz; i++) { 118 ResizeXPoly(rPoly[i],rRef,xFact,yFact); 119 } 120 } 121 122 //////////////////////////////////////////////////////////////////////////////////////////////////// 123 124 void RotatePoly(Polygon& rPoly, const Point& rRef, double sn, double cs) 125 { 126 sal_uInt16 nAnz=rPoly.GetSize(); 127 for (sal_uInt16 i=0; i<nAnz; i++) { 128 RotatePoint(rPoly[i],rRef,sn,cs); 129 } 130 } 131 132 void RotateXPoly(XPolygon& rPoly, const Point& rRef, double sn, double cs) 133 { 134 sal_uInt16 nAnz=rPoly.GetPointCount(); 135 for (sal_uInt16 i=0; i<nAnz; i++) { 136 RotatePoint(rPoly[i],rRef,sn,cs); 137 } 138 } 139 140 void RotatePoly(PolyPolygon& rPoly, const Point& rRef, double sn, double cs) 141 { 142 sal_uInt16 nAnz=rPoly.Count(); 143 for (sal_uInt16 i=0; i<nAnz; i++) { 144 RotatePoly(rPoly[i],rRef,sn,cs); 145 } 146 } 147 148 void RotateXPoly(XPolyPolygon& rPoly, const Point& rRef, double sn, double cs) 149 { 150 sal_uInt16 nAnz=rPoly.Count(); 151 for (sal_uInt16 i=0; i<nAnz; i++) { 152 RotateXPoly(rPoly[i],rRef,sn,cs); 153 } 154 } 155 156 //////////////////////////////////////////////////////////////////////////////////////////////////// 157 158 void MirrorRect(Rectangle& rRect, const Point& /*rRef1*/, const Point& /*rRef2*/, FASTBOOL bNoJustify) 159 { 160 // !!! fehlende Implementation !!! 161 if (!bNoJustify) rRect.Justify(); 162 } 163 164 void MirrorPoint(Point& rPnt, const Point& rRef1, const Point& rRef2) 165 { 166 long mx=rRef2.X()-rRef1.X(); 167 long my=rRef2.Y()-rRef1.Y(); 168 if (mx==0) { // Achse senkrecht 169 long dx=rRef1.X()-rPnt.X(); 170 rPnt.X()+=2*dx; 171 } else if (my==0) { // Achse waagerecht 172 long dy=rRef1.Y()-rPnt.Y(); 173 rPnt.Y()+=2*dy; 174 } else if (mx==my) { // Achse diagonal '\' 175 long dx1=rPnt.X()-rRef1.X(); 176 long dy1=rPnt.Y()-rRef1.Y(); 177 rPnt.X()=rRef1.X()+dy1; 178 rPnt.Y()=rRef1.Y()+dx1; 179 } else if (mx==-my) { // Achse diagonal '/' 180 long dx1=rPnt.X()-rRef1.X(); 181 long dy1=rPnt.Y()-rRef1.Y(); 182 rPnt.X()=rRef1.X()-dy1; 183 rPnt.Y()=rRef1.Y()-dx1; 184 } else { // beliebige Achse 185 // mal optimieren !!! 186 // Lot auf der Spiegelachse faellen oder so 187 long nRefWink=GetAngle(rRef2-rRef1); 188 rPnt-=rRef1; 189 long nPntWink=GetAngle(rPnt); 190 long nWink=2*(nRefWink-nPntWink); 191 double a=nWink*nPi180; 192 double nSin=sin(a); 193 double nCos=cos(a); 194 RotatePoint(rPnt,Point(),nSin,nCos); 195 rPnt+=rRef1; 196 } 197 } 198 199 void MirrorPoly(Polygon& rPoly, const Point& rRef1, const Point& rRef2) 200 { 201 sal_uInt16 nAnz=rPoly.GetSize(); 202 for (sal_uInt16 i=0; i<nAnz; i++) { 203 MirrorPoint(rPoly[i],rRef1,rRef2); 204 } 205 } 206 207 void MirrorXPoly(XPolygon& rPoly, const Point& rRef1, const Point& rRef2) 208 { 209 sal_uInt16 nAnz=rPoly.GetPointCount(); 210 for (sal_uInt16 i=0; i<nAnz; i++) { 211 MirrorPoint(rPoly[i],rRef1,rRef2); 212 } 213 } 214 215 void MirrorPoly(PolyPolygon& rPoly, const Point& rRef1, const Point& rRef2) 216 { 217 sal_uInt16 nAnz=rPoly.Count(); 218 for (sal_uInt16 i=0; i<nAnz; i++) { 219 MirrorPoly(rPoly[i],rRef1,rRef2); 220 } 221 } 222 223 void MirrorXPoly(XPolyPolygon& rPoly, const Point& rRef1, const Point& rRef2) 224 { 225 sal_uInt16 nAnz=rPoly.Count(); 226 for (sal_uInt16 i=0; i<nAnz; i++) { 227 MirrorXPoly(rPoly[i],rRef1,rRef2); 228 } 229 } 230 231 //////////////////////////////////////////////////////////////////////////////////////////////////// 232 233 void ShearPoly(Polygon& rPoly, const Point& rRef, double tn, FASTBOOL bVShear) 234 { 235 sal_uInt16 nAnz=rPoly.GetSize(); 236 for (sal_uInt16 i=0; i<nAnz; i++) { 237 ShearPoint(rPoly[i],rRef,tn,bVShear); 238 } 239 } 240 241 void ShearXPoly(XPolygon& rPoly, const Point& rRef, double tn, FASTBOOL bVShear) 242 { 243 sal_uInt16 nAnz=rPoly.GetPointCount(); 244 for (sal_uInt16 i=0; i<nAnz; i++) { 245 ShearPoint(rPoly[i],rRef,tn,bVShear); 246 } 247 } 248 249 void ShearPoly(PolyPolygon& rPoly, const Point& rRef, double tn, FASTBOOL bVShear) 250 { 251 sal_uInt16 nAnz=rPoly.Count(); 252 for (sal_uInt16 i=0; i<nAnz; i++) { 253 ShearPoly(rPoly[i],rRef,tn,bVShear); 254 } 255 } 256 257 void ShearXPoly(XPolyPolygon& rPoly, const Point& rRef, double tn, FASTBOOL bVShear) 258 { 259 sal_uInt16 nAnz=rPoly.Count(); 260 for (sal_uInt16 i=0; i<nAnz; i++) { 261 ShearXPoly(rPoly[i],rRef,tn,bVShear); 262 } 263 } 264 265 //////////////////////////////////////////////////////////////////////////////////////////////////// 266 // 267 // @@@@ @@@@@ @@@@ @@@@ @@ @@ 268 // @@ @@ @@ @@ @@ @@ @@ @@ @@ @@ 269 // @@ @@ @@ @@ @@ @@ @@ @@ @@ 270 // @@ @@@@@ @@ @@ @@ @@ @@@@ 271 // @@ @@ @@ @@ @@ @@ @@ @@ @@ 272 // @@ @@ @@ @@ @@ @@ @@ @@ @@ @@ 273 // @@@@ @@ @@ @@@@ @@@@ @@ @@ 274 // 275 //////////////////////////////////////////////////////////////////////////////////////////////////// 276 277 double CrookRotateXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter, 278 const Point& rRad, double& rSin, double& rCos, FASTBOOL bVert) 279 { 280 FASTBOOL bC1=pC1!=NULL; 281 FASTBOOL bC2=pC2!=NULL; 282 long x0=rPnt.X(); 283 long y0=rPnt.Y(); 284 long cx=rCenter.X(); 285 long cy=rCenter.Y(); 286 double nWink=GetCrookAngle(rPnt,rCenter,rRad,bVert); 287 double sn=sin(nWink); 288 double cs=cos(nWink); 289 RotatePoint(rPnt,rCenter,sn,cs); 290 if (bC1) { 291 if (bVert) { 292 // Richtung Zentrum verschieben, als Ausgangsposition fuer Rotate 293 pC1->Y()-=y0; 294 // Resize, entsprechend der Entfernung vom Zentrum 295 pC1->Y()=Round(((double)pC1->Y()) /rRad.X()*(cx-pC1->X())); 296 pC1->Y()+=cy; 297 } else { 298 // Richtung Zentrum verschieben, als Ausgangsposition fuer Rotate 299 pC1->X()-=x0; 300 // Resize, entsprechend der Entfernung vom Zentrum 301 long nPntRad=cy-pC1->Y(); 302 double nFact=(double)nPntRad/(double)rRad.Y(); 303 pC1->X()=Round((double)pC1->X()*nFact); 304 pC1->X()+=cx; 305 } 306 RotatePoint(*pC1,rCenter,sn,cs); 307 } 308 if (bC2) { 309 if (bVert) { 310 // Richtung Zentrum verschieben, als Ausgangsposition fuer Rotate 311 pC2->Y()-=y0; 312 // Resize, entsprechend der Entfernung vom Zentrum 313 pC2->Y()=Round(((double)pC2->Y()) /rRad.X()*(rCenter.X()-pC2->X())); 314 pC2->Y()+=cy; 315 } else { 316 // Richtung Zentrum verschieben, als Ausgangsposition fuer Rotate 317 pC2->X()-=x0; 318 // Resize, entsprechend der Entfernung vom Zentrum 319 long nPntRad=rCenter.Y()-pC2->Y(); 320 double nFact=(double)nPntRad/(double)rRad.Y(); 321 pC2->X()=Round((double)pC2->X()*nFact); 322 pC2->X()+=cx; 323 } 324 RotatePoint(*pC2,rCenter,sn,cs); 325 } 326 rSin=sn; 327 rCos=cs; 328 return nWink; 329 } 330 331 double CrookSlantXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter, 332 const Point& rRad, double& rSin, double& rCos, FASTBOOL bVert) 333 { 334 FASTBOOL bC1=pC1!=NULL; 335 FASTBOOL bC2=pC2!=NULL; 336 long x0=rPnt.X(); 337 long y0=rPnt.Y(); 338 long dx1=0,dy1=0; 339 long dxC1=0,dyC1=0; 340 long dxC2=0,dyC2=0; 341 if (bVert) { 342 long nStart=rCenter.X()-rRad.X(); 343 dx1=rPnt.X()-nStart; 344 rPnt.X()=nStart; 345 if (bC1) { 346 dxC1=pC1->X()-nStart; 347 pC1->X()=nStart; 348 } 349 if (bC2) { 350 dxC2=pC2->X()-nStart; 351 pC2->X()=nStart; 352 } 353 } else { 354 long nStart=rCenter.Y()-rRad.Y(); 355 dy1=rPnt.Y()-nStart; 356 rPnt.Y()=nStart; 357 if (bC1) { 358 dyC1=pC1->Y()-nStart; 359 pC1->Y()=nStart; 360 } 361 if (bC2) { 362 dyC2=pC2->Y()-nStart; 363 pC2->Y()=nStart; 364 } 365 } 366 double nWink=GetCrookAngle(rPnt,rCenter,rRad,bVert); 367 double sn=sin(nWink); 368 double cs=cos(nWink); 369 RotatePoint(rPnt,rCenter,sn,cs); 370 if (bC1) { if (bVert) pC1->Y()-=y0-rCenter.Y(); else pC1->X()-=x0-rCenter.X(); RotatePoint(*pC1,rCenter,sn,cs); } 371 if (bC2) { if (bVert) pC2->Y()-=y0-rCenter.Y(); else pC2->X()-=x0-rCenter.X(); RotatePoint(*pC2,rCenter,sn,cs); } 372 if (bVert) { 373 rPnt.X()+=dx1; 374 if (bC1) pC1->X()+=dxC1; 375 if (bC2) pC2->X()+=dxC2; 376 } else { 377 rPnt.Y()+=dy1; 378 if (bC1) pC1->Y()+=dyC1; 379 if (bC2) pC2->Y()+=dyC2; 380 } 381 rSin=sn; 382 rCos=cs; 383 return nWink; 384 } 385 386 double CrookStretchXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter, 387 const Point& rRad, double& rSin, double& rCos, FASTBOOL bVert, 388 const Rectangle rRefRect) 389 { 390 //FASTBOOL bC1=pC1!=NULL; 391 //FASTBOOL bC2=pC2!=NULL; 392 //long x0=rPnt.X(); 393 long y0=rPnt.Y(); 394 CrookSlantXPoint(rPnt,pC1,pC2,rCenter,rRad,rSin,rCos,bVert); 395 if (bVert) { 396 } else { 397 //long nBase=rCenter.Y()-rRad.Y(); 398 long nTop=rRefRect.Top(); 399 long nBtm=rRefRect.Bottom(); 400 long nHgt=nBtm-nTop; 401 long dy=rPnt.Y()-y0; 402 //FASTBOOL bOben=rRad.Y()<0; 403 double a=((double)(y0-nTop))/nHgt; 404 a*=dy; 405 rPnt.Y()=y0+Round(a); 406 } return 0.0; 407 } 408 409 //////////////////////////////////////////////////////////////////////////////////////////////////// 410 411 void CrookRotatePoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, FASTBOOL bVert) 412 { 413 double nSin,nCos; 414 sal_uInt16 nPointAnz=rPoly.GetPointCount(); 415 sal_uInt16 i=0; 416 while (i<nPointAnz) { 417 Point* pPnt=&rPoly[i]; 418 Point* pC1=NULL; 419 Point* pC2=NULL; 420 if (i+1<nPointAnz && rPoly.IsControl(i)) { // Kontrollpunkt links 421 pC1=pPnt; 422 i++; 423 pPnt=&rPoly[i]; 424 } 425 i++; 426 if (i<nPointAnz && rPoly.IsControl(i)) { // Kontrollpunkt rechts 427 pC2=&rPoly[i]; 428 i++; 429 } 430 CrookRotateXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert); 431 } 432 } 433 434 void CrookSlantPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, FASTBOOL bVert) 435 { 436 double nSin,nCos; 437 sal_uInt16 nPointAnz=rPoly.GetPointCount(); 438 sal_uInt16 i=0; 439 while (i<nPointAnz) { 440 Point* pPnt=&rPoly[i]; 441 Point* pC1=NULL; 442 Point* pC2=NULL; 443 if (i+1<nPointAnz && rPoly.IsControl(i)) { // Kontrollpunkt links 444 pC1=pPnt; 445 i++; 446 pPnt=&rPoly[i]; 447 } 448 i++; 449 if (i<nPointAnz && rPoly.IsControl(i)) { // Kontrollpunkt rechts 450 pC2=&rPoly[i]; 451 i++; 452 } 453 CrookSlantXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert); 454 } 455 } 456 457 void CrookStretchPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, FASTBOOL bVert, const Rectangle rRefRect) 458 { 459 double nSin,nCos; 460 sal_uInt16 nPointAnz=rPoly.GetPointCount(); 461 sal_uInt16 i=0; 462 while (i<nPointAnz) { 463 Point* pPnt=&rPoly[i]; 464 Point* pC1=NULL; 465 Point* pC2=NULL; 466 if (i+1<nPointAnz && rPoly.IsControl(i)) { // Kontrollpunkt links 467 pC1=pPnt; 468 i++; 469 pPnt=&rPoly[i]; 470 } 471 i++; 472 if (i<nPointAnz && rPoly.IsControl(i)) { // Kontrollpunkt rechts 473 pC2=&rPoly[i]; 474 i++; 475 } 476 CrookStretchXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert,rRefRect); 477 } 478 } 479 480 //////////////////////////////////////////////////////////////////////////////////////////////////// 481 482 void CrookRotatePoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, FASTBOOL bVert) 483 { 484 sal_uInt16 nPolyAnz=rPoly.Count(); 485 for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyAnz; nPolyNum++) { 486 CrookRotatePoly(rPoly[nPolyNum],rCenter,rRad,bVert); 487 } 488 } 489 490 void CrookSlantPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, FASTBOOL bVert) 491 { 492 sal_uInt16 nPolyAnz=rPoly.Count(); 493 for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyAnz; nPolyNum++) { 494 CrookSlantPoly(rPoly[nPolyNum],rCenter,rRad,bVert); 495 } 496 } 497 498 void CrookStretchPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, FASTBOOL bVert, const Rectangle rRefRect) 499 { 500 sal_uInt16 nPolyAnz=rPoly.Count(); 501 for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyAnz; nPolyNum++) { 502 CrookStretchPoly(rPoly[nPolyNum],rCenter,rRad,bVert,rRefRect); 503 } 504 } 505 506 //////////////////////////////////////////////////////////////////////////////////////////////////// 507 508 long GetAngle(const Point& rPnt) 509 { 510 long a=0; 511 if (rPnt.Y()==0) { 512 if (rPnt.X()<0) a=-18000; 513 } else if (rPnt.X()==0) { 514 if (rPnt.Y()>0) a=-9000; 515 else a=9000; 516 } else { 517 a=Round((atan2((double)-rPnt.Y(),(double)rPnt.X())/nPi180)); 518 } 519 return a; 520 } 521 522 long NormAngle180(long a) 523 { 524 while (a<18000) a+=36000; 525 while (a>=18000) a-=36000; 526 return a; 527 } 528 529 long NormAngle360(long a) 530 { 531 while (a<0) a+=36000; 532 while (a>=36000) a-=36000; 533 return a; 534 } 535 536 sal_uInt16 GetAngleSector(long nWink) 537 { 538 while (nWink<0) nWink+=36000; 539 while (nWink>=36000) nWink-=36000; 540 if (nWink< 9000) return 0; 541 if (nWink<18000) return 1; 542 if (nWink<27000) return 2; 543 return 3; 544 } 545 546 long GetLen(const Point& rPnt) 547 { 548 long x=Abs(rPnt.X()); 549 long y=Abs(rPnt.Y()); 550 if (x+y<0x8000) { // weil 7FFF * 7FFF * 2 = 7FFE0002 551 x*=x; 552 y*=y; 553 x+=y; 554 x=Round(sqrt((double)x)); 555 return x; 556 } else { 557 double nx=x; 558 double ny=y; 559 nx*=nx; 560 ny*=ny; 561 nx+=ny; 562 nx=sqrt(nx); 563 if (nx>0x7FFFFFFF) { 564 return 0x7FFFFFFF; // Ueberlauf, mehr is nich! 565 } else { 566 return Round(nx); 567 } 568 } 569 } 570 571 //////////////////////////////////////////////////////////////////////////////////////////////////// 572 573 void GeoStat::RecalcSinCos() 574 { 575 if (nDrehWink==0) { 576 nSin=0.0; 577 nCos=1.0; 578 } else { 579 double a=nDrehWink*nPi180; 580 nSin=sin(a); 581 nCos=cos(a); 582 } 583 } 584 585 void GeoStat::RecalcTan() 586 { 587 if (nShearWink==0) { 588 nTan=0.0; 589 } else { 590 double a=nShearWink*nPi180; 591 nTan=tan(a); 592 } 593 } 594 595 //////////////////////////////////////////////////////////////////////////////////////////////////// 596 597 Polygon Rect2Poly(const Rectangle& rRect, const GeoStat& rGeo) 598 { 599 Polygon aPol(5); 600 aPol[0]=rRect.TopLeft(); 601 aPol[1]=rRect.TopRight(); 602 aPol[2]=rRect.BottomRight(); 603 aPol[3]=rRect.BottomLeft(); 604 aPol[4]=rRect.TopLeft(); 605 if (rGeo.nShearWink!=0) ShearPoly(aPol,rRect.TopLeft(),rGeo.nTan); 606 if (rGeo.nDrehWink!=0) RotatePoly(aPol,rRect.TopLeft(),rGeo.nSin,rGeo.nCos); 607 return aPol; 608 } 609 610 void Poly2Rect(const Polygon& rPol, Rectangle& rRect, GeoStat& rGeo) 611 { 612 rGeo.nDrehWink=GetAngle(rPol[1]-rPol[0]); 613 rGeo.nDrehWink=NormAngle360(rGeo.nDrehWink); 614 // Drehung ist damit im Kasten 615 rGeo.RecalcSinCos(); 616 617 Point aPt1(rPol[1]-rPol[0]); 618 if (rGeo.nDrehWink!=0) RotatePoint(aPt1,Point(0,0),-rGeo.nSin,rGeo.nCos); // -Sin fuer Rueckdrehung 619 long nWdt=aPt1.X(); 620 621 Point aPt0(rPol[0]); 622 Point aPt3(rPol[3]-rPol[0]); 623 if (rGeo.nDrehWink!=0) RotatePoint(aPt3,Point(0,0),-rGeo.nSin,rGeo.nCos); // -Sin fuer Rueckdrehung 624 long nHgt=aPt3.Y(); 625 626 if(aPt3.X()) 627 { 628 // #i74358# the axes are not orthogonal, so for getting the correct height, 629 // calculate the length of aPt3 630 631 // #i74358# this change was wrong, in the field of the old geometry stuff 632 // it is not an error. The new height always is the same as before; shear 633 // does not change object height at all. This is different from the interactions, 634 // but obviously wanted in the old versions. 635 // 636 // nHgt = static_cast< long >(sqrt(static_cast< double >(aPt3.X() * aPt3.X() + aPt3.Y() * aPt3.Y()))); 637 } 638 639 long nShW=GetAngle(aPt3); 640 nShW-=27000; // ShearWink wird zur Senkrechten gemessen 641 nShW=-nShW; // Negieren, denn '+' ist Rechtskursivierung 642 643 FASTBOOL bMirr=aPt3.Y()<0; 644 if (bMirr) { // "Punktetausch" bei Spiegelung 645 nHgt=-nHgt; 646 nShW+=18000; 647 aPt0=rPol[3]; 648 } 649 nShW=NormAngle180(nShW); 650 if (nShW<-9000 || nShW>9000) { 651 nShW=NormAngle180(nShW+18000); 652 } 653 if (nShW<-SDRMAXSHEAR) nShW=-SDRMAXSHEAR; // ShearWinkel begrenzen auf +/- 89.00 deg 654 if (nShW>SDRMAXSHEAR) nShW=SDRMAXSHEAR; 655 rGeo.nShearWink=nShW; 656 rGeo.RecalcTan(); 657 Point aRU(aPt0); 658 aRU.X()+=nWdt; 659 aRU.Y()+=nHgt; 660 rRect=Rectangle(aPt0,aRU); 661 } 662 663 //////////////////////////////////////////////////////////////////////////////////////////////////// 664 665 void OrthoDistance8(const Point& rPt0, Point& rPt, FASTBOOL bBigOrtho) 666 { 667 long dx=rPt.X()-rPt0.X(); 668 long dy=rPt.Y()-rPt0.Y(); 669 long dxa=Abs(dx); 670 long dya=Abs(dy); 671 if (dx==0 || dy==0 || dxa==dya) return; 672 if (dxa>=dya*2) { rPt.Y()=rPt0.Y(); return; } 673 if (dya>=dxa*2) { rPt.X()=rPt0.X(); return; } 674 if ((dxa<dya) != bBigOrtho) { 675 rPt.Y()=rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ); 676 } else { 677 rPt.X()=rPt0.X()+(dya* (dx>=0 ? 1 : -1) ); 678 } 679 } 680 681 void OrthoDistance4(const Point& rPt0, Point& rPt, FASTBOOL bBigOrtho) 682 { 683 long dx=rPt.X()-rPt0.X(); 684 long dy=rPt.Y()-rPt0.Y(); 685 long dxa=Abs(dx); 686 long dya=Abs(dy); 687 if ((dxa<dya) != bBigOrtho) { 688 rPt.Y()=rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ); 689 } else { 690 rPt.X()=rPt0.X()+(dya* (dx>=0 ? 1 : -1) ); 691 } 692 } 693 694 //////////////////////////////////////////////////////////////////////////////////////////////////// 695 696 long BigMulDiv(long nVal, long nMul, long nDiv) 697 { 698 BigInt aVal(nVal); 699 aVal*=nMul; 700 if (aVal.IsNeg()!=(nDiv<0)) { 701 aVal-=nDiv/2; // fuer korrektes Runden 702 } else { 703 aVal+=nDiv/2; // fuer korrektes Runden 704 } 705 if(nDiv) 706 { 707 aVal/=nDiv; 708 return long(aVal); 709 } 710 return 0x7fffffff; 711 } 712 713 void Kuerzen(Fraction& rF, unsigned nDigits) 714 { 715 sal_Int32 nMul=rF.GetNumerator(); 716 sal_Int32 nDiv=rF.GetDenominator(); 717 FASTBOOL bNeg=sal_False; 718 if (nMul<0) { nMul=-nMul; bNeg=!bNeg; } 719 if (nDiv<0) { nDiv=-nDiv; bNeg=!bNeg; } 720 if (nMul==0 || nDiv==0) return; 721 sal_uInt32 a; 722 a=sal_uInt32(nMul); unsigned nMulZ=0; // Fuehrende Nullen zaehlen 723 while (a<0x00800000) { nMulZ+=8; a<<=8; } 724 while (a<0x80000000) { nMulZ++; a<<=1; } 725 a=sal_uInt32(nDiv); unsigned nDivZ=0; // Fuehrende Nullen zaehlen 726 while (a<0x00800000) { nDivZ+=8; a<<=8; } 727 while (a<0x80000000) { nDivZ++; a<<=1; } 728 // Anzahl der verwendeten Digits bestimmen 729 int nMulDigits=32-nMulZ; 730 int nDivDigits=32-nDivZ; 731 // Nun bestimmen, wieviele Stellen hinten weg koennen 732 int nMulWeg=nMulDigits-nDigits; if (nMulWeg<0) nMulWeg=0; 733 int nDivWeg=nDivDigits-nDigits; if (nDivWeg<0) nDivWeg=0; 734 int nWeg=Min(nMulWeg,nDivWeg); 735 nMul>>=nWeg; 736 nDiv>>=nWeg; 737 if (nMul==0 || nDiv==0) { 738 DBG_WARNING("Oups, beim kuerzen einer Fraction hat sich Joe verrechnet."); 739 return; 740 } 741 if (bNeg) nMul=-nMul; 742 rF=Fraction(nMul,nDiv); 743 } 744 745 //////////////////////////////////////////////////////////////////////////////////////////////////// 746 // Wieviele eU-Einheiten passen in einen mm bzw. Inch? 747 // Oder wie gross ist ein eU in mm bzw. Inch, und davon der Kehrwert 748 749 FrPair GetInchOrMM(MapUnit eU) 750 { 751 switch (eU) { 752 case MAP_1000TH_INCH: return FrPair(1000,1); 753 case MAP_100TH_INCH : return FrPair( 100,1); 754 case MAP_10TH_INCH : return FrPair( 10,1); 755 case MAP_INCH : return FrPair( 1,1); 756 case MAP_POINT : return FrPair( 72,1); 757 case MAP_TWIP : return FrPair(1440,1); 758 case MAP_100TH_MM : return FrPair( 100,1); 759 case MAP_10TH_MM : return FrPair( 10,1); 760 case MAP_MM : return FrPair( 1,1); 761 case MAP_CM : return FrPair( 1,10); 762 case MAP_PIXEL : { 763 VirtualDevice aVD; 764 aVD.SetMapMode(MapMode(MAP_100TH_MM)); 765 Point aP(aVD.PixelToLogic(Point(64,64))); // 64 Pixel fuer bessere Genauigkeit 766 return FrPair(6400,aP.X(),6400,aP.Y()); 767 } 768 case MAP_APPFONT: case MAP_SYSFONT: { 769 VirtualDevice aVD; 770 aVD.SetMapMode(MapMode(eU)); 771 Point aP(aVD.LogicToPixel(Point(32,32))); // 32 Einheiten fuer bessere Genauigkeit 772 aVD.SetMapMode(MapMode(MAP_100TH_MM)); 773 aP=aVD.PixelToLogic(aP); 774 return FrPair(3200,aP.X(),3200,aP.Y()); 775 } 776 default: break; 777 } 778 return Fraction(1,1); 779 } 780 781 FrPair GetInchOrMM(FieldUnit eU) 782 { 783 switch (eU) { 784 case FUNIT_INCH : return FrPair( 1,1); 785 case FUNIT_POINT : return FrPair( 72,1); 786 case FUNIT_TWIP : return FrPair(1440,1); 787 case FUNIT_100TH_MM : return FrPair( 100,1); 788 case FUNIT_MM : return FrPair( 1,1); 789 case FUNIT_CM : return FrPair( 1,10); 790 case FUNIT_M : return FrPair( 1,1000); 791 case FUNIT_KM : return FrPair( 1,1000000); 792 case FUNIT_PICA : return FrPair( 6,1); 793 case FUNIT_FOOT : return FrPair( 1,12); 794 case FUNIT_MILE : return FrPair( 1,63360); 795 default: break; 796 } 797 return Fraction(1,1); 798 } 799 800 // Den Faktor berechnen, der anzuwenden ist um n Einheiten von eS nach 801 // eD umzurechnen. Z.B. GetMapFactor(UNIT_MM,UNIT_100TH_MM) => 100. 802 803 FrPair GetMapFactor(MapUnit eS, MapUnit eD) 804 { 805 if (eS==eD) return FrPair(1,1,1,1); 806 FrPair aS(GetInchOrMM(eS)); 807 FrPair aD(GetInchOrMM(eD)); 808 FASTBOOL bSInch=IsInch(eS); 809 FASTBOOL bDInch=IsInch(eD); 810 FrPair aRet(aD.X()/aS.X(),aD.Y()/aS.Y()); 811 if (bSInch && !bDInch) { aRet.X()*=Fraction(127,5); aRet.Y()*=Fraction(127,5); } 812 if (!bSInch && bDInch) { aRet.X()*=Fraction(5,127); aRet.Y()*=Fraction(5,127); } 813 return aRet; 814 }; 815 816 FrPair GetMapFactor(MapUnit eS, FieldUnit eD) 817 { 818 FrPair aS(GetInchOrMM(eS)); 819 FrPair aD(GetInchOrMM(eD)); 820 FASTBOOL bSInch=IsInch(eS); 821 FASTBOOL bDInch=IsInch(eD); 822 FrPair aRet(aD.X()/aS.X(),aD.Y()/aS.Y()); 823 if (bSInch && !bDInch) { aRet.X()*=Fraction(127,5); aRet.Y()*=Fraction(127,5); } 824 if (!bSInch && bDInch) { aRet.X()*=Fraction(5,127); aRet.Y()*=Fraction(5,127); } 825 return aRet; 826 }; 827 828 FrPair GetMapFactor(FieldUnit eS, MapUnit eD) 829 { 830 FrPair aS(GetInchOrMM(eS)); 831 FrPair aD(GetInchOrMM(eD)); 832 FASTBOOL bSInch=IsInch(eS); 833 FASTBOOL bDInch=IsInch(eD); 834 FrPair aRet(aD.X()/aS.X(),aD.Y()/aS.Y()); 835 if (bSInch && !bDInch) { aRet.X()*=Fraction(127,5); aRet.Y()*=Fraction(127,5); } 836 if (!bSInch && bDInch) { aRet.X()*=Fraction(5,127); aRet.Y()*=Fraction(5,127); } 837 return aRet; 838 }; 839 840 FrPair GetMapFactor(FieldUnit eS, FieldUnit eD) 841 { 842 if (eS==eD) return FrPair(1,1,1,1); 843 FrPair aS(GetInchOrMM(eS)); 844 FrPair aD(GetInchOrMM(eD)); 845 FASTBOOL bSInch=IsInch(eS); 846 FASTBOOL bDInch=IsInch(eD); 847 FrPair aRet(aD.X()/aS.X(),aD.Y()/aS.Y()); 848 if (bSInch && !bDInch) { aRet.X()*=Fraction(127,5); aRet.Y()*=Fraction(127,5); } 849 if (!bSInch && bDInch) { aRet.X()*=Fraction(5,127); aRet.Y()*=Fraction(5,127); } 850 return aRet; 851 }; 852 853 //////////////////////////////////////////////////////////////////////////////////////////////////// 854 855 // 1 mile = 8 furlong = 63.360" = 1.609.344,0mm 856 // 1 furlong = 10 chains = 7.920" = 201.168,0mm 857 // 1 chain = 4 poles = 792" = 20.116,8mm 858 // 1 pole = 5 1/2 yd = 198" = 5.029,2mm 859 // 1 yd = 3 ft = 36" = 914,4mm 860 // 1 ft = 12 " = 1" = 304,8mm 861 862 void GetMeterOrInch(MapUnit eMU, short& rnKomma, long& rnMul, long& rnDiv, int& rbMetr, int& rbInch) 863 { 864 rnMul=1; rnDiv=1; 865 short nKomma=0; 866 FASTBOOL bMetr=sal_False,bInch=sal_False; 867 switch (eMU) { 868 // Metrisch 869 case MAP_100TH_MM : bMetr=sal_True; nKomma=5; break; 870 case MAP_10TH_MM : bMetr=sal_True; nKomma=4; break; 871 case MAP_MM : bMetr=sal_True; nKomma=3; break; 872 case MAP_CM : bMetr=sal_True; nKomma=2; break; 873 // Inch 874 case MAP_1000TH_INCH: bInch=sal_True; nKomma=3; break; 875 case MAP_100TH_INCH : bInch=sal_True; nKomma=2; break; 876 case MAP_10TH_INCH : bInch=sal_True; nKomma=1; break; 877 case MAP_INCH : bInch=sal_True; nKomma=0; break; 878 case MAP_POINT : bInch=sal_True; rnDiv=72; break; // 1Pt = 1/72" 879 case MAP_TWIP : bInch=sal_True; rnDiv=144; nKomma=1; break; // 1Twip = 1/1440" 880 // Sonstiges 881 case MAP_PIXEL : break; 882 case MAP_SYSFONT : break; 883 case MAP_APPFONT : break; 884 case MAP_RELATIVE : break; 885 default: break; 886 } // switch 887 rnKomma=nKomma; 888 rbMetr=bMetr; 889 rbInch=bInch; 890 } 891 892 void GetMeterOrInch(FieldUnit eFU, short& rnKomma, long& rnMul, long& rnDiv, int& rbMetr, int& rbInch) 893 { 894 rnMul=1; rnDiv=1; 895 short nKomma=0; 896 FASTBOOL bMetr=sal_False,bInch=sal_False; 897 switch (eFU) { 898 case FUNIT_NONE : break; 899 // Metrisch 900 case FUNIT_100TH_MM : bMetr=sal_True; nKomma=5; break; 901 case FUNIT_MM : bMetr=sal_True; nKomma=3; break; 902 case FUNIT_CM : bMetr=sal_True; nKomma=2; break; 903 case FUNIT_M : bMetr=sal_True; nKomma=0; break; 904 case FUNIT_KM : bMetr=sal_True; nKomma=-3; break; 905 // Inch 906 case FUNIT_TWIP : bInch=sal_True; rnDiv=144; nKomma=1; break; // 1Twip = 1/1440" 907 case FUNIT_POINT : bInch=sal_True; rnDiv=72; break; // 1Pt = 1/72" 908 case FUNIT_PICA : bInch=sal_True; rnDiv=6; break; // 1Pica = 1/6" ? 909 case FUNIT_INCH : bInch=sal_True; break; // 1" = 1" 910 case FUNIT_FOOT : bInch=sal_True; rnMul=12; break; // 1Ft = 12" 911 case FUNIT_MILE : bInch=sal_True; rnMul=6336; nKomma=-1; break; // 1mile = 63360" 912 // sonstiges 913 case FUNIT_CUSTOM : break; 914 case FUNIT_PERCENT : nKomma=2; break; 915 } // switch 916 rnKomma=nKomma; 917 rbMetr=bMetr; 918 rbInch=bInch; 919 } 920 921 void SdrFormatter::Undirty() 922 { 923 if (aScale.GetNumerator()==0 || aScale.GetDenominator()==0) aScale=Fraction(1,1); 924 FASTBOOL bSrcMetr,bSrcInch,bDstMetr,bDstInch; 925 long nMul1,nDiv1,nMul2,nDiv2; 926 short nKomma1,nKomma2; 927 // Zunaechst normalisieren auf m bzw. " 928 if (!bSrcFU) { 929 GetMeterOrInch(eSrcMU,nKomma1,nMul1,nDiv1,bSrcMetr,bSrcInch); 930 } else { 931 GetMeterOrInch(eSrcFU,nKomma1,nMul1,nDiv1,bSrcMetr,bSrcInch); 932 } 933 if (!bDstFU) { 934 GetMeterOrInch(eDstMU,nKomma2,nMul2,nDiv2,bDstMetr,bDstInch); 935 } else { 936 GetMeterOrInch(eDstFU,nKomma2,nMul2,nDiv2,bDstMetr,bDstInch); 937 } 938 nMul1*=nDiv2; 939 nDiv1*=nMul2; 940 nKomma1=nKomma1-nKomma2; 941 942 if (bSrcInch && bDstMetr) { 943 nKomma1+=4; 944 nMul1*=254; 945 } 946 if (bSrcMetr && bDstInch) { 947 nKomma1-=4; 948 nDiv1*=254; 949 } 950 951 // Temporaere Fraction zum Kuerzen 952 Fraction aTempFract(nMul1,nDiv1); 953 nMul1=aTempFract.GetNumerator(); 954 nDiv1=aTempFract.GetDenominator(); 955 956 nMul_=nMul1; 957 nDiv_=nDiv1; 958 nKomma_=nKomma1; 959 bDirty=sal_False; 960 } 961 962 963 void SdrFormatter::TakeStr(long nVal, XubString& rStr) const 964 { 965 sal_Unicode aNullCode('0'); 966 967 if(!nVal) 968 { 969 rStr = UniString(); 970 rStr += aNullCode; 971 return; 972 } 973 974 // Hier fallen trotzdem evtl. Nachkommastellen weg, wg. MulDiv statt Real 975 sal_Bool bNeg(nVal < 0); 976 SvtSysLocale aSysLoc; 977 const LocaleDataWrapper& rLoc = aSysLoc.GetLocaleData(); 978 979 ForceUndirty(); 980 981 sal_Int16 nK(nKomma_); 982 XubString aStr; 983 984 if(bNeg) 985 nVal = -nVal; 986 987 while(nK <= -3) 988 { 989 nVal *= 1000; 990 nK += 3; 991 } 992 993 while(nK <= -1) 994 { 995 nVal *= 10; 996 nK++; 997 } 998 999 if(nMul_ != nDiv_) 1000 nVal = BigMulDiv(nVal, nMul_, nDiv_); 1001 1002 aStr = UniString::CreateFromInt32(nVal); 1003 1004 if(nK > 0 && aStr.Len() <= nK ) 1005 { 1006 // Komma erforderlich 1007 sal_Int16 nAnz(nK - aStr.Len()); 1008 1009 if(nAnz >= 0 && rLoc.isNumLeadingZero()) 1010 nAnz++; 1011 1012 for(xub_StrLen i=0; i<nAnz; i++) 1013 aStr.Insert(aNullCode, 0); 1014 1015 // zuviele Nachkommastellen abhacken 1016 xub_StrLen nNumDigits(rLoc.getNumDigits()); 1017 xub_StrLen nWeg(nK - nNumDigits); 1018 1019 if(nWeg > 0) 1020 { 1021 // hier muesste eigentlich noch gerundet werden! 1022 aStr.Erase(aStr.Len() - nWeg); 1023 nK = nNumDigits; 1024 } 1025 } 1026 1027 // Vorkommastellen fuer spaeter merken 1028 xub_StrLen nVorKomma(aStr.Len() - nK); 1029 1030 if(nK > 0) 1031 { 1032 // KommaChar einfuegen 1033 // erstmal trailing Zeros abhacken 1034 while(nK > 0 && aStr.GetChar(aStr.Len() - 1) == aNullCode) 1035 { 1036 aStr.Erase(aStr.Len() - 1); 1037 nK--; 1038 } 1039 1040 if(nK > 0) 1041 { 1042 // na, noch Nachkommastellen da? 1043 sal_Unicode cDec(rLoc.getNumDecimalSep().GetChar(0)); 1044 aStr.Insert(cDec, nVorKomma); 1045 } 1046 } 1047 1048 // ggf. Trennpunkte bei jedem Tausender einfuegen 1049 if( nVorKomma > 3 ) 1050 { 1051 String aThoSep( rLoc.getNumThousandSep() ); 1052 if ( aThoSep.Len() > 0 ) 1053 { 1054 sal_Unicode cTho( aThoSep.GetChar(0) ); 1055 sal_Int32 i(nVorKomma - 3); 1056 1057 while(i > 0) 1058 { 1059 rStr.Insert(cTho, (xub_StrLen)i); 1060 i -= 3; 1061 } 1062 } 1063 } 1064 1065 if(!aStr.Len()) 1066 aStr += aNullCode; 1067 1068 if(bNeg && (aStr.Len() > 1 || aStr.GetChar(0) != aNullCode)) 1069 { 1070 rStr.Insert(sal_Unicode('-'), 0); 1071 } 1072 1073 rStr = aStr; 1074 } 1075 1076 void SdrFormatter::TakeUnitStr(MapUnit eUnit, XubString& rStr) 1077 { 1078 const sal_Char* pText; 1079 1080 switch(eUnit) 1081 { 1082 // metric units 1083 case MAP_100TH_MM : pText = "/100mm"; break; 1084 case MAP_10TH_MM : pText = "/10mm"; break; 1085 case MAP_MM : pText = "mm"; break; 1086 case MAP_CM : pText = "cm"; break; 1087 1088 // imperial units 1089 case MAP_1000TH_INCH: pText = "/1000\""; break; 1090 case MAP_100TH_INCH : pText = "/100\""; break; 1091 case MAP_10TH_INCH : pText = "/10\""; break; 1092 case MAP_INCH : pText = "\""; break; 1093 case MAP_POINT : pText = "pt"; break; 1094 case MAP_TWIP : pText = "twip"; break; 1095 1096 // other units 1097 case MAP_PIXEL : pText = "pixel"; break; 1098 case MAP_SYSFONT : pText = "sysfont"; break; 1099 case MAP_APPFONT : pText = "appfont"; break; 1100 case MAP_RELATIVE : pText = "\%"; break; 1101 1102 default : pText = ""; break; 1103 } 1104 1105 rStr = XubString::CreateFromAscii( pText ); 1106 } 1107 1108 void SdrFormatter::TakeUnitStr(FieldUnit eUnit, XubString& rStr) 1109 { 1110 const sal_Char* pText; 1111 1112 switch(eUnit) 1113 { 1114 // metric units 1115 case FUNIT_100TH_MM : pText = "/100mm"; break; 1116 case FUNIT_MM : pText = "mm"; break; 1117 case FUNIT_CM : pText = "cm"; break; 1118 case FUNIT_M : pText = "m"; break; 1119 case FUNIT_KM : pText = "km"; break; 1120 1121 // imperial units 1122 case FUNIT_TWIP : pText = "twip"; break; 1123 case FUNIT_POINT : pText = "pt"; break; 1124 case FUNIT_PICA : pText = "pica"; break; 1125 case FUNIT_INCH : pText = "\""; break; 1126 case FUNIT_FOOT : pText = "ft"; break; 1127 case FUNIT_MILE : pText = "mile(s)"; break; 1128 1129 // other units 1130 case FUNIT_PERCENT: pText = "\%"; break; 1131 1132 // case FUNIT_NONE : 1133 // case FUNIT_CUSTOM : 1134 default : pText = ""; break; 1135 } 1136 1137 rStr = XubString::CreateFromAscii( pText ); 1138 } 1139 1140 //////////////////////////////////////////////////////////////////////////////////////////////////// 1141 1142 1143