1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_drawinglayer.hxx" 30 31 #include <drawinglayer/primitive3d/sdrextrudelathetools3d.hxx> 32 #include <basegfx/polygon/b2dpolypolygon.hxx> 33 #include <basegfx/range/b2drange.hxx> 34 #include <basegfx/polygon/b2dpolypolygontools.hxx> 35 #include <basegfx/matrix/b2dhommatrix.hxx> 36 #include <basegfx/point/b3dpoint.hxx> 37 #include <basegfx/polygon/b3dpolygon.hxx> 38 #include <basegfx/polygon/b3dpolygontools.hxx> 39 #include <basegfx/polygon/b3dpolypolygontools.hxx> 40 #include <basegfx/range/b3drange.hxx> 41 #include <basegfx/matrix/b3dhommatrix.hxx> 42 #include <basegfx/polygon/b2dpolygontools.hxx> 43 #include <drawinglayer/geometry/viewinformation3d.hxx> 44 #include <numeric> 45 46 ////////////////////////////////////////////////////////////////////////////// 47 // decompositon helpers for extrude/lathe (rotation) objects 48 49 namespace 50 { 51 ////////////////////////////////////////////////////////////////////////////// 52 // common helpers 53 54 basegfx::B2DPolyPolygon impScalePolyPolygonOnCenter( 55 const basegfx::B2DPolyPolygon& rSource, 56 double fScale) 57 { 58 basegfx::B2DPolyPolygon aRetval(rSource); 59 60 if(!basegfx::fTools::equalZero(fScale)) 61 { 62 const basegfx::B2DRange aRange(basegfx::tools::getRange(rSource)); 63 const basegfx::B2DPoint aCenter(aRange.getCenter()); 64 basegfx::B2DHomMatrix aTrans; 65 66 aTrans.translate(-aCenter.getX(), -aCenter.getY()); 67 aTrans.scale(fScale, fScale); 68 aTrans.translate(aCenter.getX(), aCenter.getY()); 69 aRetval.transform(aTrans); 70 } 71 72 return aRetval; 73 } 74 75 void impGetOuterPolyPolygon( 76 basegfx::B2DPolyPolygon& rPolygon, 77 basegfx::B2DPolyPolygon& rOuterPolyPolygon, 78 double fOffset, 79 bool bCharacterMode) 80 { 81 rOuterPolyPolygon = rPolygon; 82 83 if(basegfx::fTools::more(fOffset, 0.0)) 84 { 85 if(bCharacterMode) 86 { 87 // grow the outside polygon and scale all polygons to original size. This is done 88 // to avoid a shrink which potentially would lead to self-intersections, but changes 89 // the original polygon -> not a precision step, so e.g. not usable for charts 90 const basegfx::B2DRange aRange(basegfx::tools::getRange(rPolygon)); 91 rPolygon = basegfx::tools::growInNormalDirection(rPolygon, fOffset); 92 const basegfx::B2DRange aGrownRange(basegfx::tools::getRange(rPolygon)); 93 const double fScaleX(basegfx::fTools::equalZero(aGrownRange.getWidth()) ? 1.0 : aRange.getWidth() / aGrownRange.getWidth()); 94 const double fScaleY(basegfx::fTools::equalZero(aGrownRange.getHeight())? 1.0 : aRange.getHeight() / aGrownRange.getHeight()); 95 basegfx::B2DHomMatrix aScaleTrans; 96 97 aScaleTrans.translate(-aGrownRange.getMinX(), -aGrownRange.getMinY()); 98 aScaleTrans.scale(fScaleX, fScaleY); 99 aScaleTrans.translate(aRange.getMinX(), aRange.getMinY()); 100 rPolygon.transform(aScaleTrans); 101 rOuterPolyPolygon.transform(aScaleTrans); 102 } 103 else 104 { 105 // use more precision, shrink the outer polygons. Since this may lead to self-intersections, 106 // some kind of correction should be applied here after that step 107 rOuterPolyPolygon = basegfx::tools::growInNormalDirection(rPolygon, -fOffset); 108 basegfx::tools::correctGrowShrinkPolygonPair(rPolygon, rOuterPolyPolygon); 109 } 110 } 111 } 112 113 void impAddInBetweenFill( 114 basegfx::B3DPolyPolygon& rTarget, 115 const basegfx::B3DPolyPolygon& rPolA, 116 const basegfx::B3DPolyPolygon& rPolB, 117 double fTexVerStart, 118 double fTexVerStop, 119 bool bCreateNormals, 120 bool bCreateTextureCoordinates) 121 { 122 OSL_ENSURE(rPolA.count() == rPolB.count(), "impAddInBetweenFill: unequally sized polygons (!)"); 123 const sal_uInt32 nPolygonCount(rPolA.count()); 124 125 for(sal_uInt32 a(0L); a < nPolygonCount; a++) 126 { 127 const basegfx::B3DPolygon aSubA(rPolA.getB3DPolygon(a)); 128 const basegfx::B3DPolygon aSubB(rPolB.getB3DPolygon(a)); 129 OSL_ENSURE(aSubA.count() == aSubB.count(), "impAddInBetweenFill: unequally sized polygons (!)"); 130 const sal_uInt32 nPointCount(aSubA.count()); 131 132 if(nPointCount) 133 { 134 const sal_uInt32 nEdgeCount(aSubA.isClosed() ? nPointCount : nPointCount - 1L); 135 double fTexHorMultiplicatorA(0.0), fTexHorMultiplicatorB(0.0); 136 double fPolygonPosA(0.0), fPolygonPosB(0.0); 137 138 if(bCreateTextureCoordinates) 139 { 140 const double fPolygonLengthA(basegfx::tools::getLength(aSubA)); 141 fTexHorMultiplicatorA = basegfx::fTools::equalZero(fPolygonLengthA) ? 1.0 : 1.0 / fPolygonLengthA; 142 143 const double fPolygonLengthB(basegfx::tools::getLength(aSubB)); 144 fTexHorMultiplicatorB = basegfx::fTools::equalZero(fPolygonLengthB) ? 1.0 : 1.0 / fPolygonLengthB; 145 } 146 147 for(sal_uInt32 b(0L); b < nEdgeCount; b++) 148 { 149 const sal_uInt32 nIndexA(b); 150 const sal_uInt32 nIndexB((b + 1L) % nPointCount); 151 152 const basegfx::B3DPoint aStartA(aSubA.getB3DPoint(nIndexA)); 153 const basegfx::B3DPoint aEndA(aSubA.getB3DPoint(nIndexB)); 154 const basegfx::B3DPoint aStartB(aSubB.getB3DPoint(nIndexA)); 155 const basegfx::B3DPoint aEndB(aSubB.getB3DPoint(nIndexB)); 156 157 basegfx::B3DPolygon aNew; 158 aNew.setClosed(true); 159 160 aNew.append(aStartA); 161 aNew.append(aStartB); 162 aNew.append(aEndB); 163 aNew.append(aEndA); 164 165 if(bCreateNormals) 166 { 167 aNew.setNormal(0L, aSubA.getNormal(nIndexA)); 168 aNew.setNormal(1L, aSubB.getNormal(nIndexA)); 169 aNew.setNormal(2L, aSubB.getNormal(nIndexB)); 170 aNew.setNormal(3L, aSubA.getNormal(nIndexB)); 171 } 172 173 if(bCreateTextureCoordinates) 174 { 175 const double fRelTexAL(fPolygonPosA * fTexHorMultiplicatorA); 176 const double fEdgeLengthA(basegfx::B3DVector(aEndA - aStartA).getLength()); 177 fPolygonPosA += fEdgeLengthA; 178 const double fRelTexAR(fPolygonPosA * fTexHorMultiplicatorA); 179 180 const double fRelTexBL(fPolygonPosB * fTexHorMultiplicatorB); 181 const double fEdgeLengthB(basegfx::B3DVector(aEndB - aStartB).getLength()); 182 fPolygonPosB += fEdgeLengthB; 183 const double fRelTexBR(fPolygonPosB * fTexHorMultiplicatorB); 184 185 aNew.setTextureCoordinate(0L, basegfx::B2DPoint(fRelTexAL, fTexVerStart)); 186 aNew.setTextureCoordinate(1L, basegfx::B2DPoint(fRelTexBL, fTexVerStop)); 187 aNew.setTextureCoordinate(2L, basegfx::B2DPoint(fRelTexBR, fTexVerStop)); 188 aNew.setTextureCoordinate(3L, basegfx::B2DPoint(fRelTexAR, fTexVerStart)); 189 } 190 191 rTarget.append(aNew); 192 } 193 } 194 } 195 } 196 197 void impSetNormal( 198 basegfx::B3DPolyPolygon& rCandidate, 199 const basegfx::B3DVector& rNormal) 200 { 201 for(sal_uInt32 a(0L); a < rCandidate.count(); a++) 202 { 203 basegfx::B3DPolygon aSub(rCandidate.getB3DPolygon(a)); 204 205 for(sal_uInt32 b(0L); b < aSub.count(); b++) 206 { 207 aSub.setNormal(b, rNormal); 208 } 209 210 rCandidate.setB3DPolygon(a, aSub); 211 } 212 } 213 214 void impCreateInBetweenNormals( 215 basegfx::B3DPolyPolygon& rPolA, 216 basegfx::B3DPolyPolygon& rPolB, 217 bool bSmoothHorizontalNormals) 218 { 219 OSL_ENSURE(rPolA.count() == rPolB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)"); 220 221 for(sal_uInt32 a(0L); a < rPolA.count(); a++) 222 { 223 basegfx::B3DPolygon aSubA(rPolA.getB3DPolygon(a)); 224 basegfx::B3DPolygon aSubB(rPolB.getB3DPolygon(a)); 225 OSL_ENSURE(aSubA.count() == aSubB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)"); 226 const sal_uInt32 nPointCount(aSubA.count()); 227 228 if(nPointCount) 229 { 230 basegfx::B3DPoint aPrevA(aSubA.getB3DPoint(nPointCount - 1L)); 231 basegfx::B3DPoint aCurrA(aSubA.getB3DPoint(0L)); 232 const bool bClosed(aSubA.isClosed()); 233 234 for(sal_uInt32 b(0L); b < nPointCount; b++) 235 { 236 const sal_uInt32 nIndNext((b + 1L) % nPointCount); 237 const basegfx::B3DPoint aNextA(aSubA.getB3DPoint(nIndNext)); 238 const basegfx::B3DPoint aCurrB(aSubB.getB3DPoint(b)); 239 240 // vector to back 241 basegfx::B3DVector aDepth(aCurrB - aCurrA); 242 aDepth.normalize(); 243 244 if(aDepth.equalZero()) 245 { 246 // no difference, try to get depth from next point 247 const basegfx::B3DPoint aNextB(aSubB.getB3DPoint(nIndNext)); 248 aDepth = aNextB - aNextA; 249 aDepth.normalize(); 250 } 251 252 // vector to left (correct for non-closed lines) 253 const bool bFirstAndNotClosed(!bClosed && 0L == b); 254 basegfx::B3DVector aLeft(bFirstAndNotClosed ? aCurrA - aNextA : aPrevA - aCurrA); 255 aLeft.normalize(); 256 257 // create left normal 258 const basegfx::B3DVector aNormalLeft(aDepth.getPerpendicular(aLeft)); 259 260 if(bSmoothHorizontalNormals) 261 { 262 // vector to right (correct for non-closed lines) 263 const bool bLastAndNotClosed(!bClosed && b + 1L == nPointCount); 264 basegfx::B3DVector aRight(bLastAndNotClosed ? aCurrA - aPrevA : aNextA - aCurrA); 265 aRight.normalize(); 266 267 // create right normal 268 const basegfx::B3DVector aNormalRight(aRight.getPerpendicular(aDepth)); 269 270 // create smoothed in-between normal 271 basegfx::B3DVector aNewNormal(aNormalLeft + aNormalRight); 272 aNewNormal.normalize(); 273 274 // set as new normal at polygons 275 aSubA.setNormal(b, aNewNormal); 276 aSubB.setNormal(b, aNewNormal); 277 } 278 else 279 { 280 // set aNormalLeft as new normal at polygons 281 aSubA.setNormal(b, aNormalLeft); 282 aSubB.setNormal(b, aNormalLeft); 283 } 284 285 // prepare next step 286 aPrevA = aCurrA; 287 aCurrA = aNextA; 288 } 289 290 rPolA.setB3DPolygon(a, aSubA); 291 rPolB.setB3DPolygon(a, aSubB); 292 } 293 } 294 } 295 296 void impMixNormals( 297 basegfx::B3DPolyPolygon& rPolA, 298 const basegfx::B3DPolyPolygon& rPolB, 299 double fWeightA) 300 { 301 const double fWeightB(1.0 - fWeightA); 302 OSL_ENSURE(rPolA.count() == rPolB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)"); 303 304 for(sal_uInt32 a(0L); a < rPolA.count(); a++) 305 { 306 basegfx::B3DPolygon aSubA(rPolA.getB3DPolygon(a)); 307 const basegfx::B3DPolygon aSubB(rPolB.getB3DPolygon(a)); 308 OSL_ENSURE(aSubA.count() == aSubB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)"); 309 const sal_uInt32 nPointCount(aSubA.count()); 310 311 for(sal_uInt32 b(0L); b < nPointCount; b++) 312 { 313 const basegfx::B3DVector aVA(aSubA.getNormal(b) * fWeightA); 314 const basegfx::B3DVector aVB(aSubB.getNormal(b) * fWeightB); 315 basegfx::B3DVector aVNew(aVA + aVB); 316 aVNew.normalize(); 317 aSubA.setNormal(b, aVNew); 318 } 319 320 rPolA.setB3DPolygon(a, aSubA); 321 } 322 } 323 324 bool impHasCutWith(const basegfx::B2DPolygon& rPoly, const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd) 325 { 326 // polygon is closed, one of the points is a member 327 const sal_uInt32 nPointCount(rPoly.count()); 328 329 if(nPointCount) 330 { 331 basegfx::B2DPoint aCurrent(rPoly.getB2DPoint(0)); 332 const basegfx::B2DVector aVector(rEnd - rStart); 333 334 for(sal_uInt32 a(0); a < nPointCount; a++) 335 { 336 const sal_uInt32 nNextIndex((a + 1) % nPointCount); 337 const basegfx::B2DPoint aNext(rPoly.getB2DPoint(nNextIndex)); 338 const basegfx::B2DVector aEdgeVector(aNext - aCurrent); 339 340 if(basegfx::tools::findCut( 341 rStart, aVector, 342 aCurrent, aEdgeVector)) 343 { 344 return true; 345 } 346 347 aCurrent = aNext; 348 } 349 } 350 351 return false; 352 } 353 } // end of anonymous namespace 354 355 ////////////////////////////////////////////////////////////////////////////// 356 357 namespace drawinglayer 358 { 359 namespace primitive3d 360 { 361 void createLatheSlices( 362 Slice3DVector& rSliceVector, 363 const basegfx::B2DPolyPolygon& rSource, 364 double fBackScale, 365 double fDiagonal, 366 double fRotation, 367 sal_uInt32 nSteps, 368 bool bCharacterMode, 369 bool bCloseFront, 370 bool bCloseBack) 371 { 372 if(basegfx::fTools::equalZero(fRotation) || 0L == nSteps) 373 { 374 // no rotation or no steps, just one plane 375 rSliceVector.push_back(Slice3D(rSource, basegfx::B3DHomMatrix())); 376 } 377 else 378 { 379 const bool bBackScale(!basegfx::fTools::equal(fBackScale, 1.0)); 380 const bool bClosedRotation(!bBackScale && basegfx::fTools::equal(fRotation, F_2PI)); 381 basegfx::B2DPolyPolygon aFront(rSource); 382 basegfx::B2DPolyPolygon aBack(rSource); 383 basegfx::B3DHomMatrix aTransformBack; 384 basegfx::B2DPolyPolygon aOuterBack; 385 386 if(bClosedRotation) 387 { 388 bCloseFront = bCloseBack = false; 389 } 390 391 if(bBackScale) 392 { 393 // avoid null zoom 394 if(basegfx::fTools::equalZero(fBackScale)) 395 { 396 fBackScale = 0.000001; 397 } 398 399 // back is scaled compared to front, create scaled version 400 aBack = impScalePolyPolygonOnCenter(aBack, fBackScale); 401 } 402 403 if(bCloseFront || bCloseBack) 404 { 405 const basegfx::B2DRange aBaseRange(basegfx::tools::getRange(aFront)); 406 const double fOuterLength(aBaseRange.getMaxX() * fRotation); 407 const double fInnerLength(aBaseRange.getMinX() * fRotation); 408 const double fAverageLength((fOuterLength + fInnerLength) * 0.5); 409 410 if(bCloseFront) 411 { 412 const double fOffsetLen((fAverageLength / 12.0) * fDiagonal); 413 basegfx::B2DPolyPolygon aOuterFront; 414 impGetOuterPolyPolygon(aFront, aOuterFront, fOffsetLen, bCharacterMode); 415 basegfx::B3DHomMatrix aTransform; 416 aTransform.translate(0.0, 0.0, fOffsetLen); 417 rSliceVector.push_back(Slice3D(aOuterFront, aTransform, SLICETYPE3D_FRONTCAP)); 418 } 419 420 if(bCloseBack) 421 { 422 const double fOffsetLen((fAverageLength / 12.0) * fDiagonal); 423 impGetOuterPolyPolygon(aBack, aOuterBack, fOffsetLen, bCharacterMode); 424 aTransformBack.translate(0.0, 0.0, -fOffsetLen); 425 aTransformBack.rotate(0.0, fRotation, 0.0); 426 } 427 } 428 429 // add start polygon (a = 0L) 430 if(!bClosedRotation) 431 { 432 rSliceVector.push_back(Slice3D(aFront, basegfx::B3DHomMatrix())); 433 } 434 435 // create segments (a + 1 .. nSteps) 436 const double fStepSize(1.0 / (double)nSteps); 437 438 for(sal_uInt32 a(0L); a < nSteps; a++) 439 { 440 const double fStep((double)(a + 1L) * fStepSize); 441 basegfx::B2DPolyPolygon aNewPoly(bBackScale ? basegfx::tools::interpolate(aFront, aBack, fStep) : aFront); 442 basegfx::B3DHomMatrix aNewMat; 443 aNewMat.rotate(0.0, fRotation * fStep, 0.0); 444 rSliceVector.push_back(Slice3D(aNewPoly, aNewMat)); 445 } 446 447 if(bCloseBack) 448 { 449 rSliceVector.push_back(Slice3D(aOuterBack, aTransformBack, SLICETYPE3D_BACKCAP)); 450 } 451 } 452 } 453 454 void createExtrudeSlices( 455 Slice3DVector& rSliceVector, 456 const basegfx::B2DPolyPolygon& rSource, 457 double fBackScale, 458 double fDiagonal, 459 double fDepth, 460 bool bCharacterMode, 461 bool bCloseFront, 462 bool bCloseBack) 463 { 464 if(basegfx::fTools::equalZero(fDepth)) 465 { 466 // no depth, just one plane 467 rSliceVector.push_back(Slice3D(rSource, basegfx::B3DHomMatrix())); 468 } 469 else 470 { 471 // there is depth, create Polygons for front,back and their default depth positions 472 basegfx::B2DPolyPolygon aFront(rSource); 473 basegfx::B2DPolyPolygon aBack(rSource); 474 const bool bBackScale(!basegfx::fTools::equal(fBackScale, 1.0)); 475 double fZFront(fDepth); // default depth for aFront 476 double fZBack(0.0); // default depth for aBack 477 basegfx::B2DPolyPolygon aOuterBack; 478 479 if(bBackScale) 480 { 481 // avoid null zoom 482 if(basegfx::fTools::equalZero(fBackScale)) 483 { 484 fBackScale = 0.000001; 485 } 486 487 // aFront is scaled compared to aBack, create scaled version 488 aFront = impScalePolyPolygonOnCenter(aFront, fBackScale); 489 } 490 491 if(bCloseFront) 492 { 493 const double fOffset(fDepth * fDiagonal * 0.5); 494 fZFront = fDepth - fOffset; 495 basegfx::B2DPolyPolygon aOuterFront; 496 impGetOuterPolyPolygon(aFront, aOuterFront, fOffset, bCharacterMode); 497 basegfx::B3DHomMatrix aTransformFront; 498 aTransformFront.translate(0.0, 0.0, fDepth); 499 rSliceVector.push_back(Slice3D(aOuterFront, aTransformFront, SLICETYPE3D_FRONTCAP)); 500 } 501 502 if(bCloseBack) 503 { 504 const double fOffset(fDepth * fDiagonal * 0.5); 505 fZBack = fOffset; 506 impGetOuterPolyPolygon(aBack, aOuterBack, fOffset, bCharacterMode); 507 } 508 509 // add front and back polygons at evtl. changed depths 510 { 511 basegfx::B3DHomMatrix aTransformA, aTransformB; 512 513 aTransformA.translate(0.0, 0.0, fZFront); 514 rSliceVector.push_back(Slice3D(aFront, aTransformA)); 515 516 aTransformB.translate(0.0, 0.0, fZBack); 517 rSliceVector.push_back(Slice3D(aBack, aTransformB)); 518 } 519 520 if(bCloseBack) 521 { 522 rSliceVector.push_back(Slice3D(aOuterBack, basegfx::B3DHomMatrix(), SLICETYPE3D_BACKCAP)); 523 } 524 } 525 } 526 527 basegfx::B3DPolyPolygon extractHorizontalLinesFromSlice(const Slice3DVector& rSliceVector, bool bCloseHorLines) 528 { 529 basegfx::B3DPolyPolygon aRetval; 530 const sal_uInt32 nNumSlices(rSliceVector.size()); 531 532 if(nNumSlices) 533 { 534 const sal_uInt32 nSlideSubPolygonCount(rSliceVector[0].getB3DPolyPolygon().count()); 535 536 for(sal_uInt32 b(0); b < nSlideSubPolygonCount; b++) 537 { 538 const sal_uInt32 nSubPolygonPointCount(rSliceVector[0].getB3DPolyPolygon().getB3DPolygon(b).count()); 539 540 for(sal_uInt32 c(0); c < nSubPolygonPointCount; c++) 541 { 542 basegfx::B3DPolygon aNew; 543 544 for(sal_uInt32 d(0); d < nNumSlices; d++) 545 { 546 OSL_ENSURE(nSlideSubPolygonCount == rSliceVector[d].getB3DPolyPolygon().count(), 547 "Slice PolyPolygon with different Polygon count (!)"); 548 OSL_ENSURE(nSubPolygonPointCount == rSliceVector[d].getB3DPolyPolygon().getB3DPolygon(b).count(), 549 "Slice Polygon with different point count (!)"); 550 aNew.append(rSliceVector[d].getB3DPolyPolygon().getB3DPolygon(b).getB3DPoint(c)); 551 } 552 553 aNew.setClosed(bCloseHorLines); 554 aRetval.append(aNew); 555 } 556 } 557 } 558 559 return aRetval; 560 } 561 562 basegfx::B3DPolyPolygon extractVerticalLinesFromSlice(const Slice3DVector& rSliceVector) 563 { 564 basegfx::B3DPolyPolygon aRetval; 565 const sal_uInt32 nNumSlices(rSliceVector.size()); 566 567 for(sal_uInt32 a(0L); a < nNumSlices; a++) 568 { 569 aRetval.append(rSliceVector[a].getB3DPolyPolygon()); 570 } 571 572 return aRetval; 573 } 574 575 void extractPlanesFromSlice( 576 ::std::vector< basegfx::B3DPolyPolygon >& rFill, 577 const Slice3DVector& rSliceVector, 578 bool bCreateNormals, 579 bool bSmoothHorizontalNormals, 580 bool bSmoothNormals, 581 bool bSmoothLids, 582 bool bClosed, 583 double fSmoothNormalsMix, 584 double fSmoothLidsMix, 585 bool bCreateTextureCoordinates, 586 const basegfx::B2DHomMatrix& rTexTransform) 587 { 588 const sal_uInt32 nNumSlices(rSliceVector.size()); 589 590 if(nNumSlices) 591 { 592 // common parameters 593 const sal_uInt32 nLoopCount(bClosed ? nNumSlices : nNumSlices - 1L); 594 basegfx::B3DPolyPolygon aEdgeRounding; 595 sal_uInt32 a; 596 597 // tetxture parameters 598 double fInvTexHeight(1.0); 599 double fTexHeightPos(0.0); 600 double fTexStart(0.0); 601 double fTexStop(1.0); 602 ::std::vector<double> aTexHeightArray; 603 basegfx::B3DRange aTexRangeFront; 604 basegfx::B3DRange aTexRangeBack; 605 606 if(bCreateTextureCoordinates) 607 { 608 aTexRangeFront = basegfx::tools::getRange(rSliceVector[0L].getB3DPolyPolygon()); 609 aTexRangeBack = basegfx::tools::getRange(rSliceVector[nNumSlices - 1L].getB3DPolyPolygon()); 610 611 if(aTexRangeBack.getDepth() > aTexRangeBack.getWidth()) 612 { 613 // last polygon is rotated so that depth is bigger than width, exchange X and Z 614 // for making applyDefaultTextureCoordinatesParallel use Z instead of X for 615 // horizontal texture coordinate 616 aTexRangeBack = basegfx::B3DRange( 617 aTexRangeBack.getMinZ(), aTexRangeBack.getMinY(), aTexRangeBack.getMinX(), 618 aTexRangeBack.getMaxZ(), aTexRangeBack.getMaxY(), aTexRangeBack.getMaxX()); 619 } 620 621 basegfx::B3DPoint aCenter(basegfx::tools::getRange(rSliceVector[0L].getB3DPolyPolygon()).getCenter()); 622 623 for(a = 0L; a < nLoopCount; a++) 624 { 625 const basegfx::B3DPoint aNextCenter(basegfx::tools::getRange(rSliceVector[(a + 1L) % nNumSlices].getB3DPolyPolygon()).getCenter()); 626 const double fLength(basegfx::B3DVector(aNextCenter - aCenter).getLength()); 627 aTexHeightArray.push_back(fLength); 628 aCenter = aNextCenter; 629 } 630 631 const double fTexHeight(::std::accumulate(aTexHeightArray.begin(), aTexHeightArray.end(), 0.0)); 632 633 if(!basegfx::fTools::equalZero(fTexHeight)) 634 { 635 fInvTexHeight = 1.0 / fTexHeight; 636 } 637 } 638 639 if(nLoopCount) 640 { 641 for(a = 0L; a < nLoopCount; a++) 642 { 643 const Slice3D& rSliceA(rSliceVector[a]); 644 const Slice3D& rSliceB(rSliceVector[(a + 1L) % nNumSlices]); 645 const bool bAcceptPair(SLICETYPE3D_REGULAR == rSliceA.getSliceType() && SLICETYPE3D_REGULAR == rSliceB.getSliceType()); 646 basegfx::B3DPolyPolygon aPolA(rSliceA.getB3DPolyPolygon()); 647 basegfx::B3DPolyPolygon aPolB(rSliceB.getB3DPolyPolygon()); 648 649 if(bAcceptPair) 650 { 651 if(bCreateNormals) 652 { 653 impCreateInBetweenNormals(aPolB, aPolA, bSmoothHorizontalNormals); 654 } 655 656 { 657 const sal_uInt32 nIndPrev((a + nNumSlices - 1L) % nNumSlices); 658 const Slice3D& rSlicePrev(rSliceVector[nIndPrev]); 659 basegfx::B3DPolyPolygon aPrev(rSlicePrev.getB3DPolyPolygon()); 660 basegfx::B3DPolyPolygon aPolAA(rSliceA.getB3DPolyPolygon()); 661 662 if(SLICETYPE3D_FRONTCAP == rSlicePrev.getSliceType()) 663 { 664 basegfx::B3DPolyPolygon aFront(rSlicePrev.getB3DPolyPolygon()); 665 const bool bHasSlant(aPolAA != aPrev); 666 667 if(bCreateTextureCoordinates) 668 { 669 aFront = basegfx::tools::applyDefaultTextureCoordinatesParallel(aFront, aTexRangeFront); 670 } 671 672 if(bCreateNormals) 673 { 674 basegfx::B3DVector aNormal(0.0, 0.0, -1.0); 675 676 if(aFront.count()) 677 { 678 aNormal = -aFront.getB3DPolygon(0L).getNormal(); 679 } 680 681 impSetNormal(aFront, aNormal); 682 683 if(bHasSlant) 684 { 685 impCreateInBetweenNormals(aPolAA, aPrev, bSmoothHorizontalNormals); 686 687 if(bSmoothNormals) 688 { 689 // smooth and copy 690 impMixNormals(aPolA, aPolAA, fSmoothNormalsMix); 691 aPolAA = aPolA; 692 } 693 else 694 { 695 // take over from surface 696 aPolAA = aPolA; 697 } 698 699 if(bSmoothLids) 700 { 701 // smooth and copy 702 impMixNormals(aFront, aPrev, fSmoothLidsMix); 703 aPrev = aFront; 704 } 705 else 706 { 707 // take over from front 708 aPrev = aFront; 709 } 710 } 711 else 712 { 713 if(bSmoothNormals) 714 { 715 // smooth 716 impMixNormals(aPolA, aFront, fSmoothNormalsMix); 717 } 718 719 if(bSmoothLids) 720 { 721 // smooth and copy 722 impMixNormals(aFront, aPolA, fSmoothLidsMix); 723 aPolA = aFront; 724 } 725 } 726 } 727 728 if(bHasSlant) 729 { 730 if(bCreateTextureCoordinates) 731 { 732 fTexStart = fTexHeightPos * fInvTexHeight; 733 fTexStop = (fTexHeightPos - aTexHeightArray[(a + nLoopCount - 1L) % nLoopCount]) * fInvTexHeight; 734 } 735 736 impAddInBetweenFill(aEdgeRounding, aPolAA, aPrev, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates); 737 } 738 739 aFront.flip(); 740 rFill.push_back(aFront); 741 } 742 else 743 { 744 if(bCreateNormals && bSmoothNormals && (nIndPrev != a + 1L)) 745 { 746 impCreateInBetweenNormals(aPolAA, aPrev, bSmoothHorizontalNormals); 747 impMixNormals(aPolA, aPolAA, 0.5); 748 } 749 } 750 } 751 752 { 753 const sal_uInt32 nIndNext((a + 2L) % nNumSlices); 754 const Slice3D& rSliceNext(rSliceVector[nIndNext]); 755 basegfx::B3DPolyPolygon aNext(rSliceNext.getB3DPolyPolygon()); 756 basegfx::B3DPolyPolygon aPolBB(rSliceB.getB3DPolyPolygon()); 757 758 if(SLICETYPE3D_BACKCAP == rSliceNext.getSliceType()) 759 { 760 basegfx::B3DPolyPolygon aBack(rSliceNext.getB3DPolyPolygon()); 761 const bool bHasSlant(aPolBB != aNext); 762 763 if(bCreateTextureCoordinates) 764 { 765 aBack = basegfx::tools::applyDefaultTextureCoordinatesParallel(aBack, aTexRangeBack); 766 } 767 768 if(bCreateNormals) 769 { 770 const basegfx::B3DVector aNormal(aBack.count() ? aBack.getB3DPolygon(0L).getNormal() : basegfx::B3DVector(0.0, 0.0, 1.0)); 771 impSetNormal(aBack, aNormal); 772 773 if(bHasSlant) 774 { 775 impCreateInBetweenNormals(aNext, aPolBB, bSmoothHorizontalNormals); 776 777 if(bSmoothNormals) 778 { 779 // smooth and copy 780 impMixNormals(aPolB, aPolBB, fSmoothNormalsMix); 781 aPolBB = aPolB; 782 } 783 else 784 { 785 // take over from surface 786 aPolBB = aPolB; 787 } 788 789 if(bSmoothLids) 790 { 791 // smooth and copy 792 impMixNormals(aBack, aNext, fSmoothLidsMix); 793 aNext = aBack; 794 } 795 else 796 { 797 // take over from back 798 aNext = aBack; 799 } 800 } 801 else 802 { 803 if(bSmoothNormals) 804 { 805 // smooth 806 impMixNormals(aPolB, aBack, fSmoothNormalsMix); 807 } 808 809 if(bSmoothLids) 810 { 811 // smooth and copy 812 impMixNormals(aBack, aPolB, fSmoothLidsMix); 813 aPolB = aBack; 814 } 815 } 816 } 817 818 if(bHasSlant) 819 { 820 if(bCreateTextureCoordinates) 821 { 822 fTexStart = (fTexHeightPos + aTexHeightArray[a] + aTexHeightArray[(a + 1L) % nLoopCount]) * fInvTexHeight; 823 fTexStop = (fTexHeightPos + aTexHeightArray[a]) * fInvTexHeight; 824 } 825 826 impAddInBetweenFill(aEdgeRounding, aNext, aPolBB, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates); 827 } 828 829 rFill.push_back(aBack); 830 } 831 else 832 { 833 if(bCreateNormals && bSmoothNormals && (nIndNext != a)) 834 { 835 impCreateInBetweenNormals(aNext, aPolBB, bSmoothHorizontalNormals); 836 impMixNormals(aPolB, aPolBB, 0.5); 837 } 838 } 839 } 840 841 if(bCreateTextureCoordinates) 842 { 843 fTexStart = (fTexHeightPos + aTexHeightArray[a]) * fInvTexHeight; 844 fTexStop = fTexHeightPos * fInvTexHeight; 845 } 846 847 impAddInBetweenFill(aEdgeRounding, aPolB, aPolA, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates); 848 } 849 850 if(bCreateTextureCoordinates) 851 { 852 fTexHeightPos += aTexHeightArray[a]; 853 } 854 } 855 } 856 else 857 { 858 // no loop, but a single slice (1 == nNumSlices), create a filling from the single 859 // front plane 860 const Slice3D& rSlice(rSliceVector[0]); 861 basegfx::B3DPolyPolygon aFront(rSlice.getB3DPolyPolygon()); 862 863 if(bCreateTextureCoordinates) 864 { 865 aFront = basegfx::tools::applyDefaultTextureCoordinatesParallel(aFront, aTexRangeFront); 866 } 867 868 if(bCreateNormals) 869 { 870 basegfx::B3DVector aNormal(0.0, 0.0, -1.0); 871 872 if(aFront.count()) 873 { 874 aNormal = -aFront.getB3DPolygon(0L).getNormal(); 875 } 876 877 impSetNormal(aFront, aNormal); 878 } 879 880 aFront.flip(); 881 rFill.push_back(aFront); 882 } 883 884 if(bCreateTextureCoordinates) 885 { 886 aEdgeRounding.transformTextureCoordiantes(rTexTransform); 887 } 888 889 for(a = 0L; a < aEdgeRounding.count(); a++) 890 { 891 rFill.push_back(basegfx::B3DPolyPolygon(aEdgeRounding.getB3DPolygon(a))); 892 } 893 } 894 } 895 896 void createReducedOutlines( 897 const geometry::ViewInformation3D& rViewInformation, 898 const basegfx::B3DHomMatrix& rObjectTransform, 899 const basegfx::B3DPolygon& rLoopA, 900 const basegfx::B3DPolygon& rLoopB, 901 basegfx::B3DPolyPolygon& rTarget) 902 { 903 const sal_uInt32 nPointCount(rLoopA.count()); 904 905 // with idetic polygons there are no outlines 906 if(rLoopA != rLoopB) 907 { 908 if(nPointCount && nPointCount == rLoopB.count()) 909 { 910 const basegfx::B3DHomMatrix aObjectTransform(rViewInformation.getObjectToView() * rObjectTransform); 911 const basegfx::B2DPolygon a2DLoopA(basegfx::tools::createB2DPolygonFromB3DPolygon(rLoopA, aObjectTransform)); 912 const basegfx::B2DPolygon a2DLoopB(basegfx::tools::createB2DPolygonFromB3DPolygon(rLoopB, aObjectTransform)); 913 const basegfx::B2DPoint a2DCenterA(a2DLoopA.getB2DRange().getCenter()); 914 const basegfx::B2DPoint a2DCenterB(a2DLoopB.getB2DRange().getCenter()); 915 916 // without detectable Y-Axis there are no outlines 917 if(!a2DCenterA.equal(a2DCenterB)) 918 { 919 // search for outmost left and right inter-loop-edges which do not cut the loops 920 const basegfx::B2DPoint aCommonCenter(basegfx::average(a2DCenterA, a2DCenterB)); 921 const basegfx::B2DVector aAxisVector(a2DCenterA - a2DCenterB); 922 double fMaxLeft(0.0); 923 double fMaxRight(0.0); 924 sal_uInt32 nIndexLeft(0); 925 sal_uInt32 nIndexRight(0); 926 927 for(sal_uInt32 a(0); a < nPointCount; a++) 928 { 929 const basegfx::B2DPoint aStart(a2DLoopA.getB2DPoint(a)); 930 const basegfx::B2DPoint aEnd(a2DLoopB.getB2DPoint(a)); 931 const basegfx::B2DPoint aMiddle(basegfx::average(aStart, aEnd)); 932 933 if(!basegfx::tools::isInside(a2DLoopA, aMiddle)) 934 { 935 if(!basegfx::tools::isInside(a2DLoopB, aMiddle)) 936 { 937 if(!impHasCutWith(a2DLoopA, aStart, aEnd)) 938 { 939 if(!impHasCutWith(a2DLoopB, aStart, aEnd)) 940 { 941 const basegfx::B2DVector aCandidateVector(aMiddle - aCommonCenter); 942 const double fCross(aCandidateVector.cross(aAxisVector)); 943 const double fDistance(aCandidateVector.getLength()); 944 945 if(fCross > 0.0) 946 { 947 if(fDistance > fMaxLeft) 948 { 949 fMaxLeft = fDistance; 950 nIndexLeft = a; 951 } 952 } 953 else if(fCross < 0.0) 954 { 955 if(fDistance > fMaxRight) 956 { 957 fMaxRight = fDistance; 958 nIndexRight = a; 959 } 960 } 961 } 962 } 963 } 964 } 965 } 966 967 if(fMaxLeft != 0.0) 968 { 969 basegfx::B3DPolygon aToBeAdded; 970 aToBeAdded.append(rLoopA.getB3DPoint(nIndexLeft)); 971 aToBeAdded.append(rLoopB.getB3DPoint(nIndexLeft)); 972 rTarget.append(aToBeAdded); 973 } 974 975 if(fMaxRight != 0.0) 976 { 977 basegfx::B3DPolygon aToBeAdded; 978 aToBeAdded.append(rLoopA.getB3DPoint(nIndexRight)); 979 aToBeAdded.append(rLoopB.getB3DPoint(nIndexRight)); 980 rTarget.append(aToBeAdded); 981 } 982 } 983 } 984 } 985 } 986 987 } // end of namespace primitive3d 988 } // end of namespace drawinglayer 989 990 ////////////////////////////////////////////////////////////////////////////// 991 // eof 992