Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / drawinglayer / source / primitive3d / polygontubeprimitive3d.cxx
bloba0b2fe78673cd1855a86cf7c02dc162c7b1485a0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <drawinglayer/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>
31 namespace drawinglayer
33 namespace primitive3d
35 namespace // anonymous namespace
37 class TubeBuffer
39 private:
40 // data for buffered tube primitives
41 Primitive3DContainer m_aLineTubeList;
42 sal_uInt32 m_nLineTubeSegments;
43 attribute::MaterialAttribute3D m_aLineMaterial;
44 ::osl::Mutex m_aMutex;
45 public:
46 TubeBuffer()
47 : m_nLineTubeSegments(0)
51 TubeBuffer(const TubeBuffer&) = delete;
52 const TubeBuffer& operator=(const TubeBuffer&) = delete;
54 Primitive3DContainer getLineTubeSegments(
55 sal_uInt32 nSegments,
56 const attribute::MaterialAttribute3D& rMaterial)
58 // may exclusively change cached data, use mutex
59 ::osl::MutexGuard aGuard(m_aMutex);
61 if (nSegments != m_nLineTubeSegments || !(rMaterial == m_aLineMaterial))
63 m_nLineTubeSegments = nSegments;
64 m_aLineMaterial = rMaterial;
65 m_aLineTubeList = Primitive3DContainer();
68 if (m_aLineTubeList.empty() && m_nLineTubeSegments != 0)
70 const basegfx::B3DPoint aLeft(0.0, 0.0, 0.0);
71 const basegfx::B3DPoint aRight(1.0, 0.0, 0.0);
72 basegfx::B3DPoint aLastLeft(0.0, 1.0, 0.0);
73 basegfx::B3DPoint aLastRight(1.0, 1.0, 0.0);
74 basegfx::B3DHomMatrix aRot;
75 aRot.rotate(F_2PI / static_cast<double>(m_nLineTubeSegments), 0.0, 0.0);
76 m_aLineTubeList.resize(m_nLineTubeSegments);
78 for(sal_uInt32 a = 0; a < m_nLineTubeSegments; ++a)
80 const basegfx::B3DPoint aNextLeft(aRot * aLastLeft);
81 const basegfx::B3DPoint aNextRight(aRot * aLastRight);
82 basegfx::B3DPolygon aNewPolygon;
84 aNewPolygon.append(aNextLeft);
85 aNewPolygon.setNormal(0, basegfx::B3DVector(aNextLeft - aLeft));
87 aNewPolygon.append(aLastLeft);
88 aNewPolygon.setNormal(1, basegfx::B3DVector(aLastLeft - aLeft));
90 aNewPolygon.append(aLastRight);
91 aNewPolygon.setNormal(2, basegfx::B3DVector(aLastRight - aRight));
93 aNewPolygon.append(aNextRight);
94 aNewPolygon.setNormal(3, basegfx::B3DVector(aNextRight - aRight));
96 aNewPolygon.setClosed(true);
98 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
99 const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
100 m_aLineTubeList[a] = xRef;
102 aLastLeft = aNextLeft;
103 aLastRight = aNextRight;
106 return m_aLineTubeList;
110 struct theTubeBuffer :
111 public rtl::Static< TubeBuffer, theTubeBuffer > {};
113 Primitive3DContainer getLineTubeSegments(
114 sal_uInt32 nSegments,
115 const attribute::MaterialAttribute3D& rMaterial)
117 // static data for buffered tube primitives
118 TubeBuffer &rTheBuffer = theTubeBuffer::get();
119 return rTheBuffer.getLineTubeSegments(nSegments, rMaterial);
122 class CapBuffer
124 private:
125 // data for buffered cap primitives
126 Primitive3DContainer m_aLineCapList;
127 sal_uInt32 m_nLineCapSegments;
128 attribute::MaterialAttribute3D m_aLineMaterial;
129 ::osl::Mutex m_aMutex;
130 public:
131 CapBuffer()
132 : m_nLineCapSegments(0)
135 CapBuffer(const CapBuffer&) = delete;
136 const CapBuffer& operator=(const CapBuffer&) = delete;
138 Primitive3DContainer getLineCapSegments(
139 sal_uInt32 nSegments,
140 const attribute::MaterialAttribute3D& rMaterial)
142 // may exclusively change cached data, use mutex
143 ::osl::MutexGuard aGuard(m_aMutex);
145 if (nSegments != m_nLineCapSegments || !(rMaterial == m_aLineMaterial))
147 m_nLineCapSegments = nSegments;
148 m_aLineMaterial = rMaterial;
149 m_aLineCapList = Primitive3DContainer();
152 if (m_aLineCapList.empty() && m_nLineCapSegments != 0)
154 const basegfx::B3DPoint aNull(0.0, 0.0, 0.0);
155 basegfx::B3DPoint aLast(0.0, 1.0, 0.0);
156 basegfx::B3DHomMatrix aRot;
157 aRot.rotate(F_2PI / static_cast<double>(m_nLineCapSegments), 0.0, 0.0);
158 m_aLineCapList.resize(m_nLineCapSegments);
160 for(sal_uInt32 a = 0; a < m_nLineCapSegments; ++a)
162 const basegfx::B3DPoint aNext(aRot * aLast);
163 basegfx::B3DPolygon aNewPolygon;
165 aNewPolygon.append(aLast);
166 aNewPolygon.setNormal(0, basegfx::B3DVector(aLast - aNull));
168 aNewPolygon.append(aNext);
169 aNewPolygon.setNormal(1, basegfx::B3DVector(aNext - aNull));
171 aNewPolygon.append(aNull);
172 aNewPolygon.setNormal(2, basegfx::B3DVector(-1.0, 0.0, 0.0));
174 aNewPolygon.setClosed(true);
176 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
177 const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
178 m_aLineCapList[a] = xRef;
180 aLast = aNext;
184 return m_aLineCapList;
188 struct theCapBuffer :
189 public rtl::Static< CapBuffer, theCapBuffer > {};
191 Primitive3DContainer getLineCapSegments(
192 sal_uInt32 nSegments,
193 const attribute::MaterialAttribute3D& rMaterial)
195 // static data for buffered cap primitives
196 CapBuffer &rTheBuffer = theCapBuffer::get();
197 return rTheBuffer.getLineCapSegments(nSegments, rMaterial);
200 class CapRoundBuffer
202 private:
203 // data for buffered capround primitives
204 Primitive3DContainer m_aLineCapRoundList;
205 sal_uInt32 m_nLineCapRoundSegments;
206 attribute::MaterialAttribute3D m_aLineMaterial;
207 ::osl::Mutex m_aMutex;
208 public:
209 CapRoundBuffer()
210 : m_nLineCapRoundSegments(0)
213 CapRoundBuffer(const CapRoundBuffer&) = delete;
214 const CapRoundBuffer& operator=(const CapRoundBuffer&) = delete;
216 Primitive3DContainer getLineCapRoundSegments(
217 sal_uInt32 nSegments,
218 const attribute::MaterialAttribute3D& rMaterial)
220 // may exclusively change cached data, use mutex
221 ::osl::MutexGuard aGuard(m_aMutex);
223 if (nSegments != m_nLineCapRoundSegments || !(rMaterial == m_aLineMaterial))
225 m_nLineCapRoundSegments = nSegments;
226 m_aLineMaterial = rMaterial;
227 m_aLineCapRoundList = Primitive3DContainer();
230 if (m_aLineCapRoundList.empty() && m_nLineCapRoundSegments)
232 // calculate new horizontal segments
233 sal_uInt32 nVerSeg(nSegments / 2);
235 if (nVerSeg < 1)
237 nVerSeg = 1;
240 // create half-sphere; upper half of unit sphere
241 basegfx::B3DPolyPolygon aSphere(
242 basegfx::utils::createUnitSphereFillPolyPolygon(
243 nSegments,
244 nVerSeg,
245 true,
246 F_PI2, 0.0,
247 0.0, F_2PI));
248 const sal_uInt32 nCount(aSphere.count());
250 if (nCount)
252 // rotate to have sphere cap oriented to negative X-Axis; do not
253 // forget to transform normals, too
254 basegfx::B3DHomMatrix aSphereTrans;
256 aSphereTrans.rotate(0.0, 0.0, F_PI2);
257 aSphere.transform(aSphereTrans);
258 aSphere.transformNormals(aSphereTrans);
260 // realloc for primitives and create based on polygon snippets
261 m_aLineCapRoundList.resize(nCount);
263 for (sal_uInt32 a = 0; a < nCount; ++a)
265 const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
266 const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
268 // need to create one primitive per Polygon since the primitive
269 // is for planar PolyPolygons which is definitely not the case here
270 m_aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D(
271 aPartPolyPolygon,
272 rMaterial,
273 false);
278 return m_aLineCapRoundList;
283 struct theCapRoundBuffer :
284 public rtl::Static< CapRoundBuffer, theCapRoundBuffer > {};
287 Primitive3DContainer getLineCapRoundSegments(
288 sal_uInt32 nSegments,
289 const attribute::MaterialAttribute3D& rMaterial)
291 // static data for buffered cap primitives
292 CapRoundBuffer &rTheBuffer = theCapRoundBuffer::get();
293 return rTheBuffer.getLineCapRoundSegments(nSegments, rMaterial);
296 Primitive3DContainer getLineJoinSegments(
297 sal_uInt32 nSegments,
298 const attribute::MaterialAttribute3D& rMaterial,
299 double fAngle,
300 double fMiterMinimumAngle,
301 basegfx::B2DLineJoin aLineJoin)
303 // nSegments is for whole circle, adapt to half circle
304 const sal_uInt32 nVerSeg(nSegments >> 1);
305 std::vector< BasePrimitive3D* > aResultVector;
307 if(nVerSeg)
309 if(basegfx::B2DLineJoin::Round == aLineJoin)
311 // calculate new horizontal segments
312 const sal_uInt32 nHorSeg(basegfx::fround((fAngle / F_2PI) * static_cast<double>(nSegments)));
314 if(nHorSeg)
316 // create half-sphere
317 const basegfx::B3DPolyPolygon aSphere(basegfx::utils::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle));
319 for(sal_uInt32 a(0); a < aSphere.count(); a++)
321 const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
322 const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
323 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false);
324 aResultVector.push_back(pNew);
327 else
329 // fallback to bevel when there is not at least one segment hor and ver
330 aLineJoin = basegfx::B2DLineJoin::Bevel;
334 if (basegfx::B2DLineJoin::Bevel == aLineJoin ||
335 basegfx::B2DLineJoin::Miter == aLineJoin)
337 if(basegfx::B2DLineJoin::Miter == aLineJoin)
339 const double fMiterAngle(fAngle/2.0);
341 if(fMiterAngle < fMiterMinimumAngle)
343 // fallback to bevel when miter's angle is too small
344 aLineJoin = basegfx::B2DLineJoin::Bevel;
348 const double fInc(F_PI / static_cast<double>(nVerSeg));
349 const double fSin(sin(-fAngle));
350 const double fCos(cos(-fAngle));
351 const bool bMiter(basegfx::B2DLineJoin::Miter == aLineJoin);
352 const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0);
353 const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0);
354 double fPos(-F_PI2);
355 basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY;
356 basegfx::B3DPoint aCurrMiter, aNextMiter;
357 basegfx::B3DPolygon aNewPolygon, aMiterPolygon;
359 // close polygon
360 aNewPolygon.setClosed(true);
361 aMiterPolygon.setClosed(true);
363 for(sal_uInt32 a(0); a < nVerSeg; a++)
365 const bool bFirst(0 == a);
366 const bool bLast(a + 1 == nVerSeg);
368 if(bFirst || !bLast)
370 fPos += fInc;
372 aNextPointOnXY = basegfx::B3DPoint(
373 cos(fPos),
374 sin(fPos),
375 0.0);
377 aNextPointRotY = basegfx::B3DPoint(
378 aNextPointOnXY.getX() * fCos,
379 aNextPointOnXY.getY(),
380 aNextPointOnXY.getX() * fSin);
382 if(bMiter)
384 aNextMiter = basegfx::B3DPoint(
385 aNextPointOnXY.getX(),
386 aNextPointOnXY.getY(),
387 fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
391 if(bFirst)
393 aNewPolygon.clear();
395 if(bMiter)
397 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
398 aNewPolygon.append(aNextPointOnXY);
399 aNewPolygon.append(aNextMiter);
401 aMiterPolygon.clear();
402 aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
403 aMiterPolygon.append(aNextMiter);
404 aMiterPolygon.append(aNextPointRotY);
406 else
408 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
409 aNewPolygon.append(aNextPointOnXY);
410 aNewPolygon.append(aNextPointRotY);
413 else if(bLast)
415 aNewPolygon.clear();
417 if(bMiter)
419 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
420 aNewPolygon.append(aCurrMiter);
421 aNewPolygon.append(aPointOnXY);
423 aMiterPolygon.clear();
424 aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
425 aMiterPolygon.append(aPointRotY);
426 aMiterPolygon.append(aCurrMiter);
428 else
430 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
431 aNewPolygon.append(aPointRotY);
432 aNewPolygon.append(aPointOnXY);
435 else
437 aNewPolygon.clear();
439 if(bMiter)
441 aNewPolygon.append(aPointOnXY);
442 aNewPolygon.append(aNextPointOnXY);
443 aNewPolygon.append(aNextMiter);
444 aNewPolygon.append(aCurrMiter);
446 aMiterPolygon.clear();
447 aMiterPolygon.append(aCurrMiter);
448 aMiterPolygon.append(aNextMiter);
449 aMiterPolygon.append(aNextPointRotY);
450 aMiterPolygon.append(aPointRotY);
452 else
454 aNewPolygon.append(aPointRotY);
455 aNewPolygon.append(aPointOnXY);
456 aNewPolygon.append(aNextPointOnXY);
457 aNewPolygon.append(aNextPointRotY);
461 // set normals
462 for(sal_uInt32 b(0); b < aNewPolygon.count(); b++)
464 aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
467 // create primitive
468 if(aNewPolygon.count())
470 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
471 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false);
472 aResultVector.push_back(pNew);
475 if(bMiter && aMiterPolygon.count())
477 // set normals
478 for(sal_uInt32 c(0); c < aMiterPolygon.count(); c++)
480 aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c)));
483 // create primitive
484 const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
485 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false);
486 aResultVector.push_back(pNew);
489 // prepare next step
490 if(bFirst || !bLast)
492 aPointOnXY = aNextPointOnXY;
493 aPointRotY = aNextPointRotY;
495 if(bMiter)
497 aCurrMiter = aNextMiter;
504 Primitive3DContainer aRetval(aResultVector.size());
506 for(size_t a(0); a < aResultVector.size(); a++)
508 aRetval[a] = Primitive3DReference(aResultVector[a]);
511 return aRetval;
514 basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
516 // build transformation from unit vector to vector
517 basegfx::B3DHomMatrix aRetval;
519 // get applied rotations from angles in XY and in XZ (cartesian)
520 const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength()));
521 const double fRotInXZ(atan2(-rVector.getZ(), rVector.getX()));
523 // apply rotations. Rot around Z needs to be done first, so apply in two steps
524 aRetval.rotate(0.0, 0.0, fRotInXY);
525 aRetval.rotate(0.0, fRotInXZ, 0.0);
527 return aRetval;
529 } // end of anonymous namespace
530 } // end of namespace primitive3d
531 } // end of namespace drawinglayer
534 using namespace com::sun::star;
537 namespace drawinglayer
539 namespace primitive3d
541 Primitive3DContainer PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
543 const sal_uInt32 nPointCount(getB3DPolygon().count());
544 std::vector< BasePrimitive3D* > aResultVector;
546 if(nPointCount)
548 if(basegfx::fTools::more(getRadius(), 0.0))
550 const attribute::MaterialAttribute3D aMaterial(getBColor());
551 static const sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps)
552 const bool bClosed(getB3DPolygon().isClosed());
553 const bool bNoLineJoin(basegfx::B2DLineJoin::NONE == getLineJoin());
554 const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1);
555 basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1));
556 basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0));
558 for(sal_uInt32 a(0); a < nLoopCount; a++)
560 // get next data
561 const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1) % nPointCount));
562 const basegfx::B3DVector aForw(aNext - aCurr);
563 const double fForwLen(aForw.getLength());
565 if(basegfx::fTools::more(fForwLen, 0.0))
567 // find out if linecap is active
568 const bool bFirst(!a);
569 const bool bLast(a + 1 == nLoopCount);
570 const bool bLineCapPossible(!bClosed && (bFirst || bLast));
571 const bool bLineCapRound(bLineCapPossible && css::drawing::LineCap_ROUND == getLineCap());
572 const bool bLineCapSquare(bLineCapPossible && css::drawing::LineCap_SQUARE == getLineCap());
574 // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
575 basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
577 // prepare transformations for tube and cap
578 basegfx::B3DHomMatrix aTubeTrans;
579 basegfx::B3DHomMatrix aCapTrans;
581 // cap gets radius size
582 aCapTrans.scale(getRadius(), getRadius(), getRadius());
584 if(bLineCapSquare)
586 // when square line cap just prolong line segment in X, maybe 2 x radius when
587 // first and last (simple line segment)
588 const double fExtraLength(bFirst && bLast ? getRadius() * 2.0 : getRadius());
590 aTubeTrans.scale(fForwLen + fExtraLength, getRadius(), getRadius());
592 if(bFirst)
594 // correct start positions for tube and cap when first and square prolonged
595 aTubeTrans.translate(-getRadius(), 0.0, 0.0);
596 aCapTrans.translate(-getRadius(), 0.0, 0.0);
599 else
601 // normal tube size
602 aTubeTrans.scale(fForwLen, getRadius(), getRadius());
605 // rotate and translate tube and cap
606 aTubeTrans *= aRotVector;
607 aTubeTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
608 aCapTrans *= aRotVector;
609 aCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
611 if(bNoLineJoin || (!bClosed && bFirst))
613 // line start edge, build transformed primitiveVector3D
614 Primitive3DContainer aSequence;
616 if(bLineCapRound && bFirst)
618 // LineCapRound used
619 aSequence = getLineCapRoundSegments(nSegments, aMaterial);
621 else
623 // simple closing cap
624 aSequence = getLineCapSegments(nSegments, aMaterial);
627 TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aCapTrans, aSequence);
628 aResultVector.push_back(pNewTransformedA);
630 else
632 const basegfx::B3DVector aBack(aCurr - aLast);
633 const double fCross(basegfx::cross(aBack, aForw).getLength());
635 if(!basegfx::fTools::equalZero(fCross))
637 // line connect non-parallel, aBack, aForw, use getLineJoin()
638 const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. F_PI2
639 Primitive3DContainer aNewList(
640 getLineJoinSegments(
641 nSegments,
642 aMaterial,
643 fAngle,
644 getMiterMinimumAngle(),
645 getLineJoin()));
647 // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
648 basegfx::B3DHomMatrix aInvRotVector(aRotVector);
649 aInvRotVector.invert();
650 basegfx::B3DVector aTransBack(aInvRotVector * aBack);
651 const double fRotInYZ(atan2(aTransBack.getY(), aTransBack.getZ()));
653 // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
654 // Also apply usual scaling and translation
655 basegfx::B3DHomMatrix aSphereTrans;
656 aSphereTrans.rotate(0.0, F_PI2, 0.0);
657 aSphereTrans.rotate(F_PI - fRotInYZ, 0.0, 0.0);
658 aSphereTrans *= aRotVector;
659 aSphereTrans.scale(getRadius(), getRadius(), getRadius());
660 aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
662 // line start edge, build transformed primitiveVector3D
663 aResultVector.push_back(
664 new TransformPrimitive3D(
665 aSphereTrans,
666 aNewList));
670 // create line segments, build transformed primitiveVector3D
671 aResultVector.push_back(
672 new TransformPrimitive3D(
673 aTubeTrans,
674 getLineTubeSegments(nSegments, aMaterial)));
676 if(bNoLineJoin || (!bClosed && bLast))
678 // line end edge
679 basegfx::B3DHomMatrix aBackCapTrans;
681 // Mirror (line end) and radius scale
682 aBackCapTrans.rotate(0.0, F_PI, 0.0);
683 aBackCapTrans.scale(getRadius(), getRadius(), getRadius());
685 if(bLineCapSquare && bLast)
687 // correct position when square and prolonged
688 aBackCapTrans.translate(fForwLen + getRadius(), 0.0, 0.0);
690 else
692 // standard position
693 aBackCapTrans.translate(fForwLen, 0.0, 0.0);
696 // rotate and translate to destination
697 aBackCapTrans *= aRotVector;
698 aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
700 // get primitiveVector3D
701 Primitive3DContainer aSequence;
703 if(bLineCapRound && bLast)
705 // LineCapRound used
706 aSequence = getLineCapRoundSegments(nSegments, aMaterial);
708 else
710 // simple closing cap
711 aSequence = getLineCapSegments(nSegments, aMaterial);
714 aResultVector.push_back(
715 new TransformPrimitive3D(
716 aBackCapTrans,
717 aSequence));
721 // prepare next loop step
722 aLast = aCurr;
723 aCurr = aNext;
726 else
728 // create hairline
729 PolygonHairlinePrimitive3D* pNew = new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor());
730 aResultVector.push_back(pNew);
734 // prepare return value
735 Primitive3DContainer aRetval(aResultVector.size());
737 for(size_t a(0); a < aResultVector.size(); a++)
739 aRetval[a] = Primitive3DReference(aResultVector[a]);
742 return aRetval;
745 PolygonTubePrimitive3D::PolygonTubePrimitive3D(
746 const basegfx::B3DPolygon& rPolygon,
747 const basegfx::BColor& rBColor,
748 double fRadius, basegfx::B2DLineJoin aLineJoin,
749 css::drawing::LineCap aLineCap,
750 double fDegreeStepWidth,
751 double fMiterMinimumAngle)
752 : PolygonHairlinePrimitive3D(rPolygon, rBColor),
753 maLast3DDecomposition(),
754 mfRadius(fRadius),
755 mfDegreeStepWidth(fDegreeStepWidth),
756 mfMiterMinimumAngle(fMiterMinimumAngle),
757 maLineJoin(aLineJoin),
758 maLineCap(aLineCap)
762 bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
764 if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
766 const PolygonTubePrimitive3D& rCompare = static_cast<const PolygonTubePrimitive3D&>(rPrimitive);
768 return (getRadius() == rCompare.getRadius()
769 && getDegreeStepWidth() == rCompare.getDegreeStepWidth()
770 && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle()
771 && getLineJoin() == rCompare.getLineJoin()
772 && getLineCap() == rCompare.getLineCap());
775 return false;
778 Primitive3DContainer PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
780 ::osl::MutexGuard aGuard( m_aMutex );
782 if(getLast3DDecomposition().empty())
784 const Primitive3DContainer aNewSequence(impCreate3DDecomposition(rViewInformation));
785 const_cast< PolygonTubePrimitive3D* >(this)->maLast3DDecomposition = aNewSequence;
788 return getLast3DDecomposition();
791 // provide unique ID
792 ImplPrimitive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D)
794 } // end of namespace primitive3d
795 } // end of namespace drawinglayer
797 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */