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_drawinglayer.hxx" 26 27 #include <drawinglayer/primitive3d/polygontubeprimitive3d.hxx> 28 #include <drawinglayer/attribute/materialattribute3d.hxx> 29 #include <basegfx/matrix/b3dhommatrix.hxx> 30 #include <basegfx/polygon/b3dpolypolygon.hxx> 31 #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx> 32 #include <basegfx/polygon/b3dpolypolygontools.hxx> 33 #include <drawinglayer/primitive3d/transformprimitive3d.hxx> 34 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> 35 36 ////////////////////////////////////////////////////////////////////////////// 37 38 namespace drawinglayer 39 { 40 namespace primitive3d 41 { 42 namespace // anonymous namespace 43 { getLineTubeSegments(sal_uInt32 nSegments,const attribute::MaterialAttribute3D & rMaterial)44 Primitive3DSequence getLineTubeSegments( 45 sal_uInt32 nSegments, 46 const attribute::MaterialAttribute3D& rMaterial) 47 { 48 // static data for buffered tube primitives 49 static Primitive3DSequence aLineTubeList; 50 static sal_uInt32 nLineTubeSegments(0L); 51 static attribute::MaterialAttribute3D aLineMaterial; 52 53 // may exclusively change static data, use mutex 54 ::osl::Mutex m_mutex; 55 56 if(nSegments != nLineTubeSegments || !(rMaterial == aLineMaterial)) 57 { 58 nLineTubeSegments = nSegments; 59 aLineMaterial = rMaterial; 60 aLineTubeList = Primitive3DSequence(); 61 } 62 63 if(!aLineTubeList.hasElements() && 0L != nLineTubeSegments) 64 { 65 const basegfx::B3DPoint aLeft(0.0, 0.0, 0.0); 66 const basegfx::B3DPoint aRight(1.0, 0.0, 0.0); 67 basegfx::B3DPoint aLastLeft(0.0, 1.0, 0.0); 68 basegfx::B3DPoint aLastRight(1.0, 1.0, 0.0); 69 basegfx::B3DHomMatrix aRot; 70 aRot.rotate(F_2PI / (double)nLineTubeSegments, 0.0, 0.0); 71 aLineTubeList.realloc(nLineTubeSegments); 72 73 for(sal_uInt32 a(0L); a < nLineTubeSegments; a++) 74 { 75 const basegfx::B3DPoint aNextLeft(aRot * aLastLeft); 76 const basegfx::B3DPoint aNextRight(aRot * aLastRight); 77 basegfx::B3DPolygon aNewPolygon; 78 79 aNewPolygon.append(aNextLeft); 80 aNewPolygon.setNormal(0L, basegfx::B3DVector(aNextLeft - aLeft)); 81 82 aNewPolygon.append(aLastLeft); 83 aNewPolygon.setNormal(1L, basegfx::B3DVector(aLastLeft - aLeft)); 84 85 aNewPolygon.append(aLastRight); 86 aNewPolygon.setNormal(2L, basegfx::B3DVector(aLastRight - aRight)); 87 88 aNewPolygon.append(aNextRight); 89 aNewPolygon.setNormal(3L, basegfx::B3DVector(aNextRight - aRight)); 90 91 aNewPolygon.setClosed(true); 92 93 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); 94 const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, aLineMaterial, false)); 95 aLineTubeList[a] = xRef; 96 97 aLastLeft = aNextLeft; 98 aLastRight = aNextRight; 99 } 100 } 101 102 return aLineTubeList; 103 } 104 getLineCapSegments(sal_uInt32 nSegments,const attribute::MaterialAttribute3D & rMaterial)105 Primitive3DSequence getLineCapSegments( 106 sal_uInt32 nSegments, 107 const attribute::MaterialAttribute3D& rMaterial) 108 { 109 // static data for buffered tube primitives 110 static Primitive3DSequence aLineCapList; 111 static sal_uInt32 nLineCapSegments(0L); 112 static attribute::MaterialAttribute3D aLineMaterial; 113 114 // may exclusively change static data, use mutex 115 ::osl::Mutex m_mutex; 116 117 if(nSegments != nLineCapSegments || !(rMaterial == aLineMaterial)) 118 { 119 nLineCapSegments = nSegments; 120 aLineMaterial = rMaterial; 121 aLineCapList = Primitive3DSequence(); 122 } 123 124 if(!aLineCapList.hasElements() && 0L != nLineCapSegments) 125 { 126 const basegfx::B3DPoint aNull(0.0, 0.0, 0.0); 127 basegfx::B3DPoint aLast(0.0, 1.0, 0.0); 128 basegfx::B3DHomMatrix aRot; 129 aRot.rotate(F_2PI / (double)nLineCapSegments, 0.0, 0.0); 130 aLineCapList.realloc(nLineCapSegments); 131 132 for(sal_uInt32 a(0L); a < nLineCapSegments; a++) 133 { 134 const basegfx::B3DPoint aNext(aRot * aLast); 135 basegfx::B3DPolygon aNewPolygon; 136 137 aNewPolygon.append(aLast); 138 aNewPolygon.setNormal(0L, basegfx::B3DVector(aLast - aNull)); 139 140 aNewPolygon.append(aNext); 141 aNewPolygon.setNormal(1L, basegfx::B3DVector(aNext - aNull)); 142 143 aNewPolygon.append(aNull); 144 aNewPolygon.setNormal(2L, basegfx::B3DVector(-1.0, 0.0, 0.0)); 145 146 aNewPolygon.setClosed(true); 147 148 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); 149 const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, aLineMaterial, false)); 150 aLineCapList[a] = xRef; 151 152 aLast = aNext; 153 } 154 } 155 156 return aLineCapList; 157 } 158 getLineCapRoundSegments(sal_uInt32 nSegments,const attribute::MaterialAttribute3D & rMaterial)159 Primitive3DSequence getLineCapRoundSegments( 160 sal_uInt32 nSegments, 161 const attribute::MaterialAttribute3D& rMaterial) 162 { 163 // static data for buffered tube primitives 164 static Primitive3DSequence aLineCapRoundList; 165 static sal_uInt32 nLineCapRoundSegments(0); 166 static attribute::MaterialAttribute3D aLineMaterial; 167 168 // may exclusively change static data, use mutex 169 ::osl::Mutex m_mutex; 170 171 if(nSegments != nLineCapRoundSegments || !(rMaterial == aLineMaterial)) 172 { 173 nLineCapRoundSegments = nSegments; 174 aLineMaterial = rMaterial; 175 aLineCapRoundList = Primitive3DSequence(); 176 } 177 178 if(!aLineCapRoundList.hasElements() && nLineCapRoundSegments) 179 { 180 // calculate new horizontal segments 181 sal_uInt32 nVerSeg(nSegments / 2); 182 183 if(nVerSeg < 1) 184 { 185 nVerSeg = 1; 186 } 187 188 // create half-sphere; upper half of unit sphere 189 basegfx::B3DPolyPolygon aSphere( 190 basegfx::tools::createUnitSphereFillPolyPolygon( 191 nSegments, 192 nVerSeg, 193 true, 194 F_PI2, 0.0, 195 0.0, F_2PI)); 196 const sal_uInt32 nCount(aSphere.count()); 197 198 if(nCount) 199 { 200 // rotate to have sphere cap orientned to negative X-Axis; do not 201 // forget to transform normals, too 202 basegfx::B3DHomMatrix aSphereTrans; 203 204 aSphereTrans.rotate(0.0, 0.0, F_PI2); 205 aSphere.transform(aSphereTrans); 206 aSphere.transformNormals(aSphereTrans); 207 208 // realloc for primitives and create based on polygon snippets 209 aLineCapRoundList.realloc(nCount); 210 211 for(sal_uInt32 a(0); a < nCount; a++) 212 { 213 const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a)); 214 const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon); 215 216 // need to create one primitive per Polygon since the primitive 217 // is for planar PolyPolygons which is definitely not the case here 218 aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D( 219 aPartPolyPolygon, 220 rMaterial, 221 false); 222 } 223 } 224 } 225 226 return aLineCapRoundList; 227 } 228 getLineJoinSegments(sal_uInt32 nSegments,const attribute::MaterialAttribute3D & rMaterial,double fAngle,double,double fMiterMinimumAngle,basegfx::B2DLineJoin aLineJoin)229 Primitive3DSequence getLineJoinSegments( 230 sal_uInt32 nSegments, 231 const attribute::MaterialAttribute3D& rMaterial, 232 double fAngle, 233 double /*fDegreeStepWidth*/, 234 double fMiterMinimumAngle, 235 basegfx::B2DLineJoin aLineJoin) 236 { 237 // nSegments is for whole circle, adapt to half circle 238 const sal_uInt32 nVerSeg(nSegments >> 1L); 239 std::vector< BasePrimitive3D* > aResultVector; 240 241 if(nVerSeg) 242 { 243 if(basegfx::B2DLINEJOIN_ROUND == aLineJoin) 244 { 245 // calculate new horizontal segments 246 const sal_uInt32 nHorSeg(basegfx::fround((fAngle / F_2PI) * (double)nSegments)); 247 248 if(nHorSeg) 249 { 250 // create half-sphere 251 const basegfx::B3DPolyPolygon aSphere(basegfx::tools::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle)); 252 253 for(sal_uInt32 a(0L); a < aSphere.count(); a++) 254 { 255 const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a)); 256 const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon); 257 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false); 258 aResultVector.push_back(pNew); 259 } 260 } 261 else 262 { 263 // fallback to bevel when there is not at least one segment hor and ver 264 aLineJoin = basegfx::B2DLINEJOIN_BEVEL; 265 } 266 } 267 268 if(basegfx::B2DLINEJOIN_MIDDLE == aLineJoin 269 || basegfx::B2DLINEJOIN_BEVEL == aLineJoin 270 || basegfx::B2DLINEJOIN_MITER == aLineJoin) 271 { 272 if(basegfx::B2DLINEJOIN_MITER == aLineJoin) 273 { 274 const double fMiterAngle(fAngle/2.0); 275 276 if(fMiterAngle < fMiterMinimumAngle) 277 { 278 // fallback to bevel when miter's angle is too small 279 aLineJoin = basegfx::B2DLINEJOIN_BEVEL; 280 } 281 } 282 283 const double fInc(F_PI / (double)nVerSeg); 284 const double fSin(sin(-fAngle)); 285 const double fCos(cos(-fAngle)); 286 const bool bMiter(basegfx::B2DLINEJOIN_MITER == aLineJoin); 287 const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0); 288 const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0); 289 double fPos(-F_PI2); 290 basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY; 291 basegfx::B3DPoint aCurrMiter, aNextMiter; 292 basegfx::B3DPolygon aNewPolygon, aMiterPolygon; 293 294 // close polygon 295 aNewPolygon.setClosed(true); 296 aMiterPolygon.setClosed(true); 297 298 for(sal_uInt32 a(0L); a < nVerSeg; a++) 299 { 300 const bool bFirst(0L == a); 301 const bool bLast(a + 1L == nVerSeg); 302 303 if(bFirst || !bLast) 304 { 305 fPos += fInc; 306 307 aNextPointOnXY = basegfx::B3DPoint( 308 cos(fPos), 309 sin(fPos), 310 0.0); 311 312 aNextPointRotY = basegfx::B3DPoint( 313 aNextPointOnXY.getX() * fCos, 314 aNextPointOnXY.getY(), 315 aNextPointOnXY.getX() * fSin); 316 317 if(bMiter) 318 { 319 aNextMiter = basegfx::B3DPoint( 320 aNextPointOnXY.getX(), 321 aNextPointOnXY.getY(), 322 fMiterSin * (aNextPointOnXY.getX() / fMiterCos)); 323 } 324 } 325 326 if(bFirst) 327 { 328 aNewPolygon.clear(); 329 330 if(bMiter) 331 { 332 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0)); 333 aNewPolygon.append(aNextPointOnXY); 334 aNewPolygon.append(aNextMiter); 335 336 aMiterPolygon.clear(); 337 aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0)); 338 aMiterPolygon.append(aNextMiter); 339 aMiterPolygon.append(aNextPointRotY); 340 } 341 else 342 { 343 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0)); 344 aNewPolygon.append(aNextPointOnXY); 345 aNewPolygon.append(aNextPointRotY); 346 } 347 } 348 else if(bLast) 349 { 350 aNewPolygon.clear(); 351 352 if(bMiter) 353 { 354 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0)); 355 aNewPolygon.append(aCurrMiter); 356 aNewPolygon.append(aPointOnXY); 357 358 aMiterPolygon.clear(); 359 aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0)); 360 aMiterPolygon.append(aPointRotY); 361 aMiterPolygon.append(aCurrMiter); 362 } 363 else 364 { 365 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0)); 366 aNewPolygon.append(aPointRotY); 367 aNewPolygon.append(aPointOnXY); 368 } 369 } 370 else 371 { 372 aNewPolygon.clear(); 373 374 if(bMiter) 375 { 376 aNewPolygon.append(aPointOnXY); 377 aNewPolygon.append(aNextPointOnXY); 378 aNewPolygon.append(aNextMiter); 379 aNewPolygon.append(aCurrMiter); 380 381 aMiterPolygon.clear(); 382 aMiterPolygon.append(aCurrMiter); 383 aMiterPolygon.append(aNextMiter); 384 aMiterPolygon.append(aNextPointRotY); 385 aMiterPolygon.append(aPointRotY); 386 } 387 else 388 { 389 aNewPolygon.append(aPointRotY); 390 aNewPolygon.append(aPointOnXY); 391 aNewPolygon.append(aNextPointOnXY); 392 aNewPolygon.append(aNextPointRotY); 393 } 394 } 395 396 // set normals 397 for(sal_uInt32 b(0L); b < aNewPolygon.count(); b++) 398 { 399 aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b))); 400 } 401 402 // create primitive 403 if(aNewPolygon.count()) 404 { 405 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); 406 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false); 407 aResultVector.push_back(pNew); 408 } 409 410 if(bMiter && aMiterPolygon.count()) 411 { 412 // set normals 413 for(sal_uInt32 c(0L); c < aMiterPolygon.count(); c++) 414 { 415 aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c))); 416 } 417 418 // create primitive 419 const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon); 420 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false); 421 aResultVector.push_back(pNew); 422 } 423 424 // prepare next step 425 if(bFirst || !bLast) 426 { 427 aPointOnXY = aNextPointOnXY; 428 aPointRotY = aNextPointRotY; 429 430 if(bMiter) 431 { 432 aCurrMiter = aNextMiter; 433 } 434 } 435 } 436 } 437 } 438 439 Primitive3DSequence aRetval(aResultVector.size()); 440 441 for(sal_uInt32 a(0L); a < aResultVector.size(); a++) 442 { 443 aRetval[a] = Primitive3DReference(aResultVector[a]); 444 } 445 446 return aRetval; 447 } 448 getRotationFromVector(const basegfx::B3DVector & rVector)449 basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector) 450 { 451 // build transformation from unit vector to vector 452 basegfx::B3DHomMatrix aRetval; 453 454 // get applied rotations from angles in XY and in XZ (cartesian) 455 const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength())); 456 const double fRotInXZ(atan2(-rVector.getZ(), rVector.getX())); 457 458 // apply rotations. Rot around Z needs to be done first, so apply in two steps 459 aRetval.rotate(0.0, 0.0, fRotInXY); 460 aRetval.rotate(0.0, fRotInXZ, 0.0); 461 462 return aRetval; 463 } 464 } // end of anonymous namespace 465 } // end of namespace primitive3d 466 } // end of namespace drawinglayer 467 468 ////////////////////////////////////////////////////////////////////////////// 469 470 using namespace com::sun::star; 471 472 ////////////////////////////////////////////////////////////////////////////// 473 474 namespace drawinglayer 475 { 476 namespace primitive3d 477 { impCreate3DDecomposition(const geometry::ViewInformation3D &) const478 Primitive3DSequence PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const 479 { 480 const sal_uInt32 nPointCount(getB3DPolygon().count()); 481 std::vector< BasePrimitive3D* > aResultVector; 482 483 if(nPointCount) 484 { 485 if(basegfx::fTools::more(getRadius(), 0.0)) 486 { 487 const attribute::MaterialAttribute3D aMaterial(getBColor()); 488 static sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps) 489 const bool bClosed(getB3DPolygon().isClosed()); 490 const bool bNoLineJoin(basegfx::B2DLINEJOIN_NONE == getLineJoin()); 491 const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1); 492 basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1)); 493 basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0)); 494 495 for(sal_uInt32 a(0); a < nLoopCount; a++) 496 { 497 // get next data 498 const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1) % nPointCount)); 499 const basegfx::B3DVector aForw(aNext - aCurr); 500 const double fForwLen(aForw.getLength()); 501 502 if(basegfx::fTools::more(fForwLen, 0.0)) 503 { 504 // find out if linecap is active 505 const bool bFirst(!a); 506 const bool bLast(a + 1 == nLoopCount); 507 const bool bLineCapPossible(!bClosed && (bFirst || bLast)); 508 const bool bLineCapRound(bLineCapPossible && com::sun::star::drawing::LineCap_ROUND == getLineCap()); 509 const bool bLineCapSquare(bLineCapPossible && com::sun::star::drawing::LineCap_SQUARE == getLineCap()); 510 511 // get rotation from vector, this describes rotation from (1, 0, 0) to aForw 512 basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw)); 513 514 // prepare transformations for tube and cap 515 basegfx::B3DHomMatrix aTubeTrans; 516 basegfx::B3DHomMatrix aCapTrans; 517 518 // cap gets radius size 519 aCapTrans.scale(getRadius(), getRadius(), getRadius()); 520 521 if(bLineCapSquare) 522 { 523 // when square line cap just prolong line segment in X, maybe 2 x radius when 524 // first and last (simple line segment) 525 const double fExtraLength(bFirst && bLast ? getRadius() * 2.0 : getRadius()); 526 527 aTubeTrans.scale(fForwLen + fExtraLength, getRadius(), getRadius()); 528 529 if(bFirst) 530 { 531 // correct start positions for tube and cap when first and square prolonged 532 aTubeTrans.translate(-getRadius(), 0.0, 0.0); 533 aCapTrans.translate(-getRadius(), 0.0, 0.0); 534 } 535 } 536 else 537 { 538 // normal tube size 539 aTubeTrans.scale(fForwLen, getRadius(), getRadius()); 540 } 541 542 // rotate and translate tube and cap 543 aTubeTrans *= aRotVector; 544 aTubeTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); 545 aCapTrans *= aRotVector; 546 aCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); 547 548 if(bNoLineJoin || (!bClosed && bFirst)) 549 { 550 // line start edge, build transformed primitiveVector3D 551 Primitive3DSequence aSequence; 552 553 if(bLineCapRound && bFirst) 554 { 555 // LineCapRound used 556 aSequence = getLineCapRoundSegments(nSegments, aMaterial); 557 } 558 else 559 { 560 // simple closing cap 561 aSequence = getLineCapSegments(nSegments, aMaterial); 562 } 563 564 TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aCapTrans, aSequence); 565 aResultVector.push_back(pNewTransformedA); 566 } 567 else 568 { 569 const basegfx::B3DVector aBack(aCurr - aLast); 570 const double fCross(basegfx::cross(aBack, aForw).getLength()); 571 572 if(!basegfx::fTools::equalZero(fCross)) 573 { 574 // line connect non-parallel, aBack, aForw, use getLineJoin() 575 const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. F_PI2 576 Primitive3DSequence aNewList( 577 getLineJoinSegments( 578 nSegments, 579 aMaterial, 580 fAngle, 581 getDegreeStepWidth(), 582 getMiterMinimumAngle(), 583 getLineJoin())); 584 585 // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack 586 basegfx::B3DHomMatrix aInvRotVector(aRotVector); 587 aInvRotVector.invert(); 588 basegfx::B3DVector aTransBack(aInvRotVector * aBack); 589 const double fRotInYZ(atan2(aTransBack.getY(), aTransBack.getZ())); 590 591 // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X. 592 // Also apply usual scaling and translation 593 basegfx::B3DHomMatrix aSphereTrans; 594 aSphereTrans.rotate(0.0, F_PI2, 0.0); 595 aSphereTrans.rotate(F_PI - fRotInYZ, 0.0, 0.0); 596 aSphereTrans *= aRotVector; 597 aSphereTrans.scale(getRadius(), getRadius(), getRadius()); 598 aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); 599 600 // line start edge, build transformed primitiveVector3D 601 aResultVector.push_back( 602 new TransformPrimitive3D( 603 aSphereTrans, 604 aNewList)); 605 } 606 } 607 608 // create line segments, build transformed primitiveVector3D 609 aResultVector.push_back( 610 new TransformPrimitive3D( 611 aTubeTrans, 612 getLineTubeSegments(nSegments, aMaterial))); 613 614 if(bNoLineJoin || (!bClosed && bLast)) 615 { 616 // line end edge 617 basegfx::B3DHomMatrix aBackCapTrans; 618 619 // Mirror (line end) and radius scale 620 aBackCapTrans.rotate(0.0, F_PI, 0.0); 621 aBackCapTrans.scale(getRadius(), getRadius(), getRadius()); 622 623 if(bLineCapSquare && bLast) 624 { 625 // correct position when square and prolonged 626 aBackCapTrans.translate(fForwLen + getRadius(), 0.0, 0.0); 627 } 628 else 629 { 630 // standard position 631 aBackCapTrans.translate(fForwLen, 0.0, 0.0); 632 } 633 634 // rotate and translate to destination 635 aBackCapTrans *= aRotVector; 636 aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); 637 638 // get primitiveVector3D 639 Primitive3DSequence aSequence; 640 641 if(bLineCapRound && bLast) 642 { 643 // LineCapRound used 644 aSequence = getLineCapRoundSegments(nSegments, aMaterial); 645 } 646 else 647 { 648 // simple closing cap 649 aSequence = getLineCapSegments(nSegments, aMaterial); 650 } 651 652 aResultVector.push_back( 653 new TransformPrimitive3D( 654 aBackCapTrans, 655 aSequence)); 656 } 657 } 658 659 // prepare next loop step 660 aLast = aCurr; 661 aCurr = aNext; 662 } 663 } 664 else 665 { 666 // create hairline 667 PolygonHairlinePrimitive3D* pNew = new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor()); 668 aResultVector.push_back(pNew); 669 } 670 } 671 672 // prepare return value 673 Primitive3DSequence aRetval(aResultVector.size()); 674 675 for(sal_uInt32 a(0L); a < aResultVector.size(); a++) 676 { 677 aRetval[a] = Primitive3DReference(aResultVector[a]); 678 } 679 680 return aRetval; 681 } 682 PolygonTubePrimitive3D(const basegfx::B3DPolygon & rPolygon,const basegfx::BColor & rBColor,double fRadius,basegfx::B2DLineJoin aLineJoin,com::sun::star::drawing::LineCap aLineCap,double fDegreeStepWidth,double fMiterMinimumAngle)683 PolygonTubePrimitive3D::PolygonTubePrimitive3D( 684 const basegfx::B3DPolygon& rPolygon, 685 const basegfx::BColor& rBColor, 686 double fRadius, basegfx::B2DLineJoin aLineJoin, 687 com::sun::star::drawing::LineCap aLineCap, 688 double fDegreeStepWidth, 689 double fMiterMinimumAngle) 690 : PolygonHairlinePrimitive3D(rPolygon, rBColor), 691 maLast3DDecomposition(), 692 mfRadius(fRadius), 693 mfDegreeStepWidth(fDegreeStepWidth), 694 mfMiterMinimumAngle(fMiterMinimumAngle), 695 maLineJoin(aLineJoin), 696 maLineCap(aLineCap) 697 { 698 } 699 operator ==(const BasePrimitive3D & rPrimitive) const700 bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const 701 { 702 if(PolygonHairlinePrimitive3D::operator==(rPrimitive)) 703 { 704 const PolygonTubePrimitive3D& rCompare = (PolygonTubePrimitive3D&)rPrimitive; 705 706 return (getRadius() == rCompare.getRadius() 707 && getDegreeStepWidth() == rCompare.getDegreeStepWidth() 708 && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle() 709 && getLineJoin() == rCompare.getLineJoin() 710 && getLineCap() == rCompare.getLineCap()); 711 } 712 713 return false; 714 } 715 get3DDecomposition(const geometry::ViewInformation3D & rViewInformation) const716 Primitive3DSequence PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const 717 { 718 ::osl::MutexGuard aGuard( m_aMutex ); 719 720 if(!getLast3DDecomposition().hasElements()) 721 { 722 const Primitive3DSequence aNewSequence(impCreate3DDecomposition(rViewInformation)); 723 const_cast< PolygonTubePrimitive3D* >(this)->setLast3DDecomposition(aNewSequence); 724 } 725 726 return getLast3DDecomposition(); 727 } 728 729 // provide unique ID 730 ImplPrimitrive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D) 731 732 } // end of namespace primitive3d 733 } // end of namespace drawinglayer 734 735 ////////////////////////////////////////////////////////////////////////////// 736 // eof 737