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