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/sdrextrudeprimitive3d.hxx> 32 #include <basegfx/matrix/b2dhommatrix.hxx> 33 #include <basegfx/polygon/b2dpolygontools.hxx> 34 #include <basegfx/polygon/b3dpolypolygontools.hxx> 35 #include <drawinglayer/primitive3d/sdrdecompositiontools3d.hxx> 36 #include <basegfx/tools/canvastools.hxx> 37 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> 38 #include <drawinglayer/geometry/viewinformation3d.hxx> 39 #include <drawinglayer/attribute/sdrfillattribute.hxx> 40 #include <drawinglayer/attribute/sdrlineattribute.hxx> 41 #include <drawinglayer/attribute/sdrshadowattribute.hxx> 42 43 ////////////////////////////////////////////////////////////////////////////// 44 45 using namespace com::sun::star; 46 47 ////////////////////////////////////////////////////////////////////////////// 48 49 namespace drawinglayer 50 { 51 namespace primitive3d 52 { 53 Primitive3DSequence SdrExtrudePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const 54 { 55 Primitive3DSequence aRetval; 56 57 // get slices 58 const Slice3DVector& rSliceVector = getSlices(); 59 60 if(rSliceVector.size()) 61 { 62 sal_uInt32 a; 63 64 // decide what to create 65 const ::com::sun::star::drawing::NormalsKind eNormalsKind(getSdr3DObjectAttribute().getNormalsKind()); 66 const bool bCreateNormals(::com::sun::star::drawing::NormalsKind_SPECIFIC == eNormalsKind); 67 const bool bCreateTextureCoordiantesX(::com::sun::star::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX()); 68 const bool bCreateTextureCoordiantesY(::com::sun::star::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY()); 69 double fRelativeTextureWidth(1.0); 70 basegfx::B2DHomMatrix aTexTransform; 71 72 if(!getSdrLFSAttribute().getFill().isDefault() && (bCreateTextureCoordiantesX || bCreateTextureCoordiantesY)) 73 { 74 const basegfx::B2DPolygon aFirstPolygon(maCorrectedPolyPolygon.getB2DPolygon(0L)); 75 const double fFrontLength(basegfx::tools::getLength(aFirstPolygon)); 76 const double fFrontArea(basegfx::tools::getArea(aFirstPolygon)); 77 const double fSqrtFrontArea(sqrt(fFrontArea)); 78 fRelativeTextureWidth = basegfx::fTools::equalZero(fSqrtFrontArea) ? 1.0 : fFrontLength / fSqrtFrontArea; 79 fRelativeTextureWidth = (double)((sal_uInt32)(fRelativeTextureWidth - 0.5)); 80 81 if(fRelativeTextureWidth < 1.0) 82 { 83 fRelativeTextureWidth = 1.0; 84 } 85 86 aTexTransform.translate(-0.5, -0.5); 87 aTexTransform.scale(-1.0, -1.0); 88 aTexTransform.translate(0.5, 0.5); 89 aTexTransform.scale(fRelativeTextureWidth, 1.0); 90 } 91 92 // create geometry 93 ::std::vector< basegfx::B3DPolyPolygon > aFill; 94 extractPlanesFromSlice(aFill, rSliceVector, 95 bCreateNormals, getSmoothHorizontalNormals(), getSmoothNormals(), getSmoothLids(), false, 96 0.5, 0.6, bCreateTextureCoordiantesX || bCreateTextureCoordiantesY, aTexTransform); 97 98 // get full range 99 const basegfx::B3DRange aRange(getRangeFrom3DGeometry(aFill)); 100 101 // normal creation 102 if(!getSdrLFSAttribute().getFill().isDefault()) 103 { 104 if(::com::sun::star::drawing::NormalsKind_SPHERE == eNormalsKind) 105 { 106 applyNormalsKindSphereTo3DGeometry(aFill, aRange); 107 } 108 else if(::com::sun::star::drawing::NormalsKind_FLAT == eNormalsKind) 109 { 110 applyNormalsKindFlatTo3DGeometry(aFill); 111 } 112 113 if(getSdr3DObjectAttribute().getNormalsInvert()) 114 { 115 applyNormalsInvertTo3DGeometry(aFill); 116 } 117 } 118 119 // texture coordinates 120 if(!getSdrLFSAttribute().getFill().isDefault()) 121 { 122 applyTextureTo3DGeometry( 123 getSdr3DObjectAttribute().getTextureProjectionX(), 124 getSdr3DObjectAttribute().getTextureProjectionY(), 125 aFill, 126 aRange, 127 getTextureSize()); 128 } 129 130 if(!getSdrLFSAttribute().getFill().isDefault()) 131 { 132 // add fill 133 aRetval = create3DPolyPolygonFillPrimitives( 134 aFill, 135 getTransform(), 136 getTextureSize(), 137 getSdr3DObjectAttribute(), 138 getSdrLFSAttribute().getFill(), 139 getSdrLFSAttribute().getFillFloatTransGradient()); 140 } 141 else 142 { 143 // create simplified 3d hit test geometry 144 aRetval = createHiddenGeometryPrimitives3D( 145 aFill, 146 getTransform(), 147 getTextureSize(), 148 getSdr3DObjectAttribute()); 149 } 150 151 // add line 152 if(!getSdrLFSAttribute().getLine().isDefault()) 153 { 154 if(getSdr3DObjectAttribute().getReducedLineGeometry()) 155 { 156 // create geometric outlines with reduced line geometry for chart. 157 const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector)); 158 const sal_uInt32 nCount(aVerLine.count()); 159 basegfx::B3DPolyPolygon aReducedLoops; 160 basegfx::B3DPolyPolygon aNewLineGeometry; 161 162 // sort out doubles (front and back planes when no edge rounding is done). Since 163 // this is a line geometry merged from PolyPolygons, loop over all Polygons 164 for(a = 0; a < nCount; a++) 165 { 166 const sal_uInt32 nReducedCount(aReducedLoops.count()); 167 const basegfx::B3DPolygon aCandidate(aVerLine.getB3DPolygon(a)); 168 bool bAdd(true); 169 170 if(nReducedCount) 171 { 172 for(sal_uInt32 b(0); bAdd && b < nReducedCount; b++) 173 { 174 if(aCandidate == aReducedLoops.getB3DPolygon(b)) 175 { 176 bAdd = false; 177 } 178 } 179 } 180 181 if(bAdd) 182 { 183 aReducedLoops.append(aCandidate); 184 } 185 } 186 187 // from here work with reduced loops and reduced count without changing them 188 const sal_uInt32 nReducedCount(aReducedLoops.count()); 189 190 if(nReducedCount > 1) 191 { 192 for(sal_uInt32 b(1); b < nReducedCount; b++) 193 { 194 // get loop pair 195 const basegfx::B3DPolygon aCandA(aReducedLoops.getB3DPolygon(b - 1)); 196 const basegfx::B3DPolygon aCandB(aReducedLoops.getB3DPolygon(b)); 197 198 // for each loop pair create the connection edges 199 createReducedOutlines( 200 rViewInformation, 201 getTransform(), 202 aCandA, 203 aCandB, 204 aNewLineGeometry); 205 } 206 } 207 208 // add reduced loops themselves 209 aNewLineGeometry.append(aReducedLoops); 210 211 // to create vertical edges at non-C1/C2 steady loops, use maCorrectedPolyPolygon 212 // directly since the 3D Polygons do not suport this. 213 // 214 // Unfortunately there is no bezier polygon provided by the chart module; one reason is 215 // that the API for extrude wants a 3D polygon geometry (for historical reasons, i guess) 216 // and those have no beziers. Another reason is that he chart module uses self-created 217 // stuff to create the 2D geometry (in ShapeFactory::createPieSegment), but this geometry 218 // does not contain bezier infos, either. The only way which is possible for now is to 'detect' 219 // candidates for vertical edges of pie segments by looking for the angles in the polygon. 220 // 221 // This is all not very well designed ATM. Ideally, the ReducedLineGeometry is responsible 222 // for creating the outer geometry edges (createReducedOutlines), but for special edges 223 // like the vertical ones for pie center and both start/end, the incarnation with the 224 // knowledge about that it needs to create those and IS a pie segment -> in this case, 225 // the chart itself. 226 const sal_uInt32 nPolyCount(maCorrectedPolyPolygon.count()); 227 228 for(sal_uInt32 c(0); c < nPolyCount; c++) 229 { 230 const basegfx::B2DPolygon aCandidate(maCorrectedPolyPolygon.getB2DPolygon(c)); 231 const sal_uInt32 nPointCount(aCandidate.count()); 232 233 if(nPointCount > 2) 234 { 235 sal_uInt32 nIndexA(nPointCount); 236 sal_uInt32 nIndexB(nPointCount); 237 sal_uInt32 nIndexC(nPointCount); 238 239 for(sal_uInt32 d(0); d < nPointCount; d++) 240 { 241 const sal_uInt32 nPrevInd((d + nPointCount - 1) % nPointCount); 242 const sal_uInt32 nNextInd((d + 1) % nPointCount); 243 const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(d)); 244 const basegfx::B2DVector aPrev(aCandidate.getB2DPoint(nPrevInd) - aPoint); 245 const basegfx::B2DVector aNext(aCandidate.getB2DPoint(nNextInd) - aPoint); 246 const double fAngle(aPrev.angle(aNext)); 247 248 // take each angle which deviates more than 10% from going straight as 249 // special edge. This will detect the two outer edges of pie segments, 250 // but not always the center one (think about a near 180 degree pie) 251 if(F_PI - fabs(fAngle) > F_PI * 0.1) 252 { 253 if(nPointCount == nIndexA) 254 { 255 nIndexA = d; 256 } 257 else if(nPointCount == nIndexB) 258 { 259 nIndexB = d; 260 } 261 else if(nPointCount == nIndexC) 262 { 263 nIndexC = d; 264 d = nPointCount; 265 } 266 } 267 } 268 269 const bool bIndexAUsed(nIndexA != nPointCount); 270 const bool bIndexBUsed(nIndexB != nPointCount); 271 bool bIndexCUsed(nIndexC != nPointCount); 272 273 if(bIndexCUsed) 274 { 275 // already three special edges found, so the center one was already detected 276 // and does not need to be searched 277 } 278 else if(bIndexAUsed && bIndexBUsed) 279 { 280 // outer edges detected (they are approx. 90 degrees), but center one not. 281 // Look with the knowledge that it's in-between the two found ones 282 if(((nIndexA + 2) % nPointCount) == nIndexB) 283 { 284 nIndexC = (nIndexA + 1) % nPointCount; 285 } 286 else if(((nIndexA + nPointCount - 2) % nPointCount) == nIndexB) 287 { 288 nIndexC = (nIndexA + nPointCount - 1) % nPointCount; 289 } 290 291 bIndexCUsed = (nIndexC != nPointCount); 292 } 293 294 if(bIndexAUsed) 295 { 296 const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexA)); 297 const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0); 298 const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth()); 299 basegfx::B3DPolygon aToBeAdded; 300 301 aToBeAdded.append(aStart); 302 aToBeAdded.append(aEnd); 303 aNewLineGeometry.append(aToBeAdded); 304 } 305 306 if(bIndexBUsed) 307 { 308 const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexB)); 309 const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0); 310 const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth()); 311 basegfx::B3DPolygon aToBeAdded; 312 313 aToBeAdded.append(aStart); 314 aToBeAdded.append(aEnd); 315 aNewLineGeometry.append(aToBeAdded); 316 } 317 318 if(bIndexCUsed) 319 { 320 const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexC)); 321 const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0); 322 const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth()); 323 basegfx::B3DPolygon aToBeAdded; 324 325 aToBeAdded.append(aStart); 326 aToBeAdded.append(aEnd); 327 aNewLineGeometry.append(aToBeAdded); 328 } 329 } 330 } 331 332 // append loops themselves 333 aNewLineGeometry.append(aReducedLoops); 334 335 if(aNewLineGeometry.count()) 336 { 337 const Primitive3DSequence aLines(create3DPolyPolygonLinePrimitives( 338 aNewLineGeometry, getTransform(), getSdrLFSAttribute().getLine())); 339 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval, aLines); 340 } 341 } 342 else 343 { 344 // extract line geometry from slices 345 const basegfx::B3DPolyPolygon aHorLine(extractHorizontalLinesFromSlice(rSliceVector, false)); 346 const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector)); 347 348 // add horizontal lines 349 const Primitive3DSequence aHorLines(create3DPolyPolygonLinePrimitives( 350 aHorLine, getTransform(), getSdrLFSAttribute().getLine())); 351 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval, aHorLines); 352 353 // add vertical lines 354 const Primitive3DSequence aVerLines(create3DPolyPolygonLinePrimitives( 355 aVerLine, getTransform(), getSdrLFSAttribute().getLine())); 356 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval, aVerLines); 357 } 358 } 359 360 // add shadow 361 if(!getSdrLFSAttribute().getShadow().isDefault() && aRetval.hasElements()) 362 { 363 const Primitive3DSequence aShadow(createShadowPrimitive3D( 364 aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D())); 365 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval, aShadow); 366 } 367 } 368 369 return aRetval; 370 } 371 372 void SdrExtrudePrimitive3D::impCreateSlices() 373 { 374 // prepare the polygon. No double points, correct orientations and a correct 375 // outmost polygon are needed 376 maCorrectedPolyPolygon = getPolyPolygon(); 377 maCorrectedPolyPolygon.removeDoublePoints(); 378 maCorrectedPolyPolygon = basegfx::tools::correctOrientations(maCorrectedPolyPolygon); 379 maCorrectedPolyPolygon = basegfx::tools::correctOutmostPolygon(maCorrectedPolyPolygon); 380 381 // prepare slices as geometry 382 createExtrudeSlices(maSlices, maCorrectedPolyPolygon, getBackScale(), getDiagonal(), getDepth(), getCharacterMode(), getCloseFront(), getCloseBack()); 383 } 384 385 const Slice3DVector& SdrExtrudePrimitive3D::getSlices() const 386 { 387 // This can be made dependent of getSdrLFSAttribute().getFill() and getSdrLFSAttribute().getLine() 388 // again when no longer geometry is needed for non-visible 3D objects as it is now for chart 389 if(getPolyPolygon().count() && !maSlices.size()) 390 { 391 ::osl::Mutex m_mutex; 392 const_cast< SdrExtrudePrimitive3D& >(*this).impCreateSlices(); 393 } 394 395 return maSlices; 396 } 397 398 SdrExtrudePrimitive3D::SdrExtrudePrimitive3D( 399 const basegfx::B3DHomMatrix& rTransform, 400 const basegfx::B2DVector& rTextureSize, 401 const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute, 402 const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute, 403 const basegfx::B2DPolyPolygon& rPolyPolygon, 404 double fDepth, 405 double fDiagonal, 406 double fBackScale, 407 bool bSmoothNormals, 408 bool bSmoothHorizontalNormals, 409 bool bSmoothLids, 410 bool bCharacterMode, 411 bool bCloseFront, 412 bool bCloseBack) 413 : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute), 414 maCorrectedPolyPolygon(), 415 maSlices(), 416 maPolyPolygon(rPolyPolygon), 417 mfDepth(fDepth), 418 mfDiagonal(fDiagonal), 419 mfBackScale(fBackScale), 420 mpLastRLGViewInformation(0), 421 mbSmoothNormals(bSmoothNormals), 422 mbSmoothHorizontalNormals(bSmoothHorizontalNormals), 423 mbSmoothLids(bSmoothLids), 424 mbCharacterMode(bCharacterMode), 425 mbCloseFront(bCloseFront), 426 mbCloseBack(bCloseBack) 427 { 428 // make sure depth is positive 429 if(basegfx::fTools::lessOrEqual(getDepth(), 0.0)) 430 { 431 mfDepth = 0.0; 432 } 433 434 // make sure the percentage value getDiagonal() is between 0.0 and 1.0 435 if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0)) 436 { 437 mfDiagonal = 0.0; 438 } 439 else if(basegfx::fTools::moreOrEqual(getDiagonal(), 1.0)) 440 { 441 mfDiagonal = 1.0; 442 } 443 444 // no close front/back when polygon is not closed 445 if(getPolyPolygon().count() && !getPolyPolygon().getB2DPolygon(0L).isClosed()) 446 { 447 mbCloseFront = mbCloseBack = false; 448 } 449 450 // no edge rounding when not closing 451 if(!getCloseFront() && !getCloseBack()) 452 { 453 mfDiagonal = 0.0; 454 } 455 } 456 457 SdrExtrudePrimitive3D::~SdrExtrudePrimitive3D() 458 { 459 if(mpLastRLGViewInformation) 460 { 461 delete mpLastRLGViewInformation; 462 } 463 } 464 465 bool SdrExtrudePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const 466 { 467 if(SdrPrimitive3D::operator==(rPrimitive)) 468 { 469 const SdrExtrudePrimitive3D& rCompare = static_cast< const SdrExtrudePrimitive3D& >(rPrimitive); 470 471 return (getPolyPolygon() == rCompare.getPolyPolygon() 472 && getDepth() == rCompare.getDepth() 473 && getDiagonal() == rCompare.getDiagonal() 474 && getBackScale() == rCompare.getBackScale() 475 && getSmoothNormals() == rCompare.getSmoothNormals() 476 && getSmoothHorizontalNormals() == rCompare.getSmoothHorizontalNormals() 477 && getSmoothLids() == rCompare.getSmoothLids() 478 && getCharacterMode() == rCompare.getCharacterMode() 479 && getCloseFront() == rCompare.getCloseFront() 480 && getCloseBack() == rCompare.getCloseBack()); 481 } 482 483 return false; 484 } 485 486 basegfx::B3DRange SdrExtrudePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const 487 { 488 // use defaut from sdrPrimitive3D which uses transformation expanded by line width/2 489 // The parent implementation which uses the ranges of the decomposition would be more 490 // corrcet, but for historical reasons it is necessary to do the old method: To get 491 // the range of the non-transformed geometry and transform it then. This leads to different 492 // ranges where the new method is more correct, but the need to keep the old behaviour 493 // has priority here. 494 return get3DRangeFromSlices(getSlices()); 495 } 496 497 Primitive3DSequence SdrExtrudePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const 498 { 499 if(getSdr3DObjectAttribute().getReducedLineGeometry()) 500 { 501 if(!mpLastRLGViewInformation || 502 (getBuffered3DDecomposition().hasElements() 503 && *mpLastRLGViewInformation != rViewInformation)) 504 { 505 // conditions of last local decomposition with reduced lines have changed. Remember 506 // new one and clear current decompositiopn 507 ::osl::Mutex m_mutex; 508 SdrExtrudePrimitive3D* pThat = const_cast< SdrExtrudePrimitive3D* >(this); 509 pThat->setBuffered3DDecomposition(Primitive3DSequence()); 510 delete pThat->mpLastRLGViewInformation; 511 pThat->mpLastRLGViewInformation = new geometry::ViewInformation3D(rViewInformation); 512 } 513 } 514 515 // no test for buffering needed, call parent 516 return SdrPrimitive3D::get3DDecomposition(rViewInformation); 517 } 518 519 // provide unique ID 520 ImplPrimitrive3DIDBlock(SdrExtrudePrimitive3D, PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D) 521 522 } // end of namespace primitive3d 523 } // end of namespace drawinglayer 524 525 ////////////////////////////////////////////////////////////////////////////// 526 // eof 527