xref: /trunk/main/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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/polygontubeprimitive3d.hxx>
32 #include <drawinglayer/attribute/materialattribute3d.hxx>
33 #include <basegfx/matrix/b3dhommatrix.hxx>
34 #include <basegfx/polygon/b3dpolypolygon.hxx>
35 #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
36 #include <basegfx/polygon/b3dpolypolygontools.hxx>
37 #include <drawinglayer/primitive3d/transformprimitive3d.hxx>
38 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
39 
40 //////////////////////////////////////////////////////////////////////////////
41 
42 namespace drawinglayer
43 {
44     namespace primitive3d
45     {
46         namespace // anonymous namespace
47         {
48             Primitive3DSequence getLineTubeSegments(
49                 sal_uInt32 nSegments,
50                 const attribute::MaterialAttribute3D& rMaterial)
51             {
52                 // static data for buffered tube primitives
53                 static Primitive3DSequence aLineTubeList;
54                 static sal_uInt32 nLineTubeSegments(0L);
55                 static attribute::MaterialAttribute3D aLineMaterial;
56 
57                 // may exclusively change static data, use mutex
58                 ::osl::Mutex m_mutex;
59 
60                 if(nSegments != nLineTubeSegments || !(rMaterial == aLineMaterial))
61                 {
62                     nLineTubeSegments = nSegments;
63                     aLineMaterial = rMaterial;
64                     aLineTubeList = Primitive3DSequence();
65                 }
66 
67                 if(!aLineTubeList.hasElements() && 0L != nLineTubeSegments)
68                 {
69                     const basegfx::B3DPoint aLeft(0.0, 0.0, 0.0);
70                     const basegfx::B3DPoint aRight(1.0, 0.0, 0.0);
71                     basegfx::B3DPoint aLastLeft(0.0, 1.0, 0.0);
72                     basegfx::B3DPoint aLastRight(1.0, 1.0, 0.0);
73                     basegfx::B3DHomMatrix aRot;
74                     aRot.rotate(F_2PI / (double)nLineTubeSegments, 0.0, 0.0);
75                     aLineTubeList.realloc(nLineTubeSegments);
76 
77                     for(sal_uInt32 a(0L); a < nLineTubeSegments; a++)
78                     {
79                         const basegfx::B3DPoint aNextLeft(aRot * aLastLeft);
80                         const basegfx::B3DPoint aNextRight(aRot * aLastRight);
81                         basegfx::B3DPolygon aNewPolygon;
82 
83                         aNewPolygon.append(aNextLeft);
84                         aNewPolygon.setNormal(0L, basegfx::B3DVector(aNextLeft - aLeft));
85 
86                         aNewPolygon.append(aLastLeft);
87                         aNewPolygon.setNormal(1L, basegfx::B3DVector(aLastLeft - aLeft));
88 
89                         aNewPolygon.append(aLastRight);
90                         aNewPolygon.setNormal(2L, basegfx::B3DVector(aLastRight - aRight));
91 
92                         aNewPolygon.append(aNextRight);
93                         aNewPolygon.setNormal(3L, basegfx::B3DVector(aNextRight - aRight));
94 
95                         aNewPolygon.setClosed(true);
96 
97                         const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
98                         const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, aLineMaterial, false));
99                         aLineTubeList[a] = xRef;
100 
101                         aLastLeft = aNextLeft;
102                         aLastRight = aNextRight;
103                     }
104                 }
105 
106                 return aLineTubeList;
107             }
108 
109             Primitive3DSequence getLineCapSegments(
110                 sal_uInt32 nSegments,
111                 const attribute::MaterialAttribute3D& rMaterial)
112             {
113                 // static data for buffered tube primitives
114                 static Primitive3DSequence aLineCapList;
115                 static sal_uInt32 nLineCapSegments(0L);
116                 static attribute::MaterialAttribute3D aLineMaterial;
117 
118                 // may exclusively change static data, use mutex
119                 ::osl::Mutex m_mutex;
120 
121                 if(nSegments != nLineCapSegments || !(rMaterial == aLineMaterial))
122                 {
123                     nLineCapSegments = nSegments;
124                     aLineMaterial = rMaterial;
125                     aLineCapList = Primitive3DSequence();
126                 }
127 
128                 if(!aLineCapList.hasElements() && 0L != nLineCapSegments)
129                 {
130                     const basegfx::B3DPoint aNull(0.0, 0.0, 0.0);
131                     basegfx::B3DPoint aLast(0.0, 1.0, 0.0);
132                     basegfx::B3DHomMatrix aRot;
133                     aRot.rotate(F_2PI / (double)nLineCapSegments, 0.0, 0.0);
134                     aLineCapList.realloc(nLineCapSegments);
135 
136                     for(sal_uInt32 a(0L); a < nLineCapSegments; a++)
137                     {
138                         const basegfx::B3DPoint aNext(aRot * aLast);
139                         basegfx::B3DPolygon aNewPolygon;
140 
141                         aNewPolygon.append(aLast);
142                         aNewPolygon.setNormal(0L, basegfx::B3DVector(aLast - aNull));
143 
144                         aNewPolygon.append(aNext);
145                         aNewPolygon.setNormal(1L, basegfx::B3DVector(aNext - aNull));
146 
147                         aNewPolygon.append(aNull);
148                         aNewPolygon.setNormal(2L, basegfx::B3DVector(-1.0, 0.0, 0.0));
149 
150                         aNewPolygon.setClosed(true);
151 
152                         const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
153                         const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, aLineMaterial, false));
154                         aLineCapList[a] = xRef;
155 
156                         aLast = aNext;
157                     }
158                 }
159 
160                 return aLineCapList;
161             }
162 
163             Primitive3DSequence getLineJoinSegments(
164                 sal_uInt32 nSegments,
165                 const attribute::MaterialAttribute3D& rMaterial,
166                 double fAngle,
167                 double /*fDegreeStepWidth*/,
168                 double fMiterMinimumAngle,
169                 basegfx::B2DLineJoin aLineJoin)
170             {
171                 // nSegments is for whole circle, adapt to half circle
172                 const sal_uInt32 nVerSeg(nSegments >> 1L);
173                 std::vector< BasePrimitive3D* > aResultVector;
174 
175                 if(nVerSeg)
176                 {
177                     if(basegfx::B2DLINEJOIN_ROUND == aLineJoin)
178                     {
179                         // calculate new horizontal segments
180                         const sal_uInt32 nHorSeg((sal_uInt32)((fAngle / F_2PI) * (double)nSegments));
181 
182                         if(nHorSeg)
183                         {
184                             // create half-sphere
185                             const basegfx::B3DPolyPolygon aSphere(basegfx::tools::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle));
186 
187                             for(sal_uInt32 a(0L); a < aSphere.count(); a++)
188                             {
189                                 const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a));
190                                 const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
191                                 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false);
192                                 aResultVector.push_back(pNew);
193                             }
194                         }
195                         else
196                         {
197                             // fallback to bevel when there is not at least one segment hor and ver
198                             aLineJoin = basegfx::B2DLINEJOIN_BEVEL;
199                         }
200                     }
201 
202                     if(basegfx::B2DLINEJOIN_MIDDLE == aLineJoin
203                         || basegfx::B2DLINEJOIN_BEVEL == aLineJoin
204                         || basegfx::B2DLINEJOIN_MITER == aLineJoin)
205                     {
206                         if(basegfx::B2DLINEJOIN_MITER == aLineJoin)
207                         {
208                             const double fMiterAngle(fAngle/2.0);
209 
210                             if(fMiterAngle < fMiterMinimumAngle)
211                             {
212                                 // fallback to bevel when miter's angle is too small
213                                 aLineJoin = basegfx::B2DLINEJOIN_BEVEL;
214                             }
215                         }
216 
217                         const double fInc(F_PI / (double)nVerSeg);
218                         const double fSin(sin(-fAngle));
219                         const double fCos(cos(-fAngle));
220                         const bool bMiter(basegfx::B2DLINEJOIN_MITER == aLineJoin);
221                         const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0);
222                         const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0);
223                         double fPos(-F_PI2);
224                         basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY;
225                         basegfx::B3DPoint aCurrMiter, aNextMiter;
226                         basegfx::B3DPolygon aNewPolygon, aMiterPolygon;
227 
228                         // close polygon
229                         aNewPolygon.setClosed(true);
230                         aMiterPolygon.setClosed(true);
231 
232                         for(sal_uInt32 a(0L); a < nVerSeg; a++)
233                         {
234                             const bool bFirst(0L == a);
235                             const bool bLast(a + 1L == nVerSeg);
236 
237                             if(bFirst || !bLast)
238                             {
239                                 fPos += fInc;
240 
241                                 aNextPointOnXY = basegfx::B3DPoint(
242                                     cos(fPos),
243                                     sin(fPos),
244                                     0.0);
245 
246                                 aNextPointRotY = basegfx::B3DPoint(
247                                     aNextPointOnXY.getX() * fCos,
248                                     aNextPointOnXY.getY(),
249                                     aNextPointOnXY.getX() * fSin);
250 
251                                 if(bMiter)
252                                 {
253                                     aNextMiter = basegfx::B3DPoint(
254                                         aNextPointOnXY.getX(),
255                                         aNextPointOnXY.getY(),
256                                         fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
257                                 }
258                             }
259 
260                             if(bFirst)
261                             {
262                                 aNewPolygon.clear();
263 
264                                 if(bMiter)
265                                 {
266                                     aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
267                                     aNewPolygon.append(aNextPointOnXY);
268                                     aNewPolygon.append(aNextMiter);
269 
270                                     aMiterPolygon.clear();
271                                     aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
272                                     aMiterPolygon.append(aNextMiter);
273                                     aMiterPolygon.append(aNextPointRotY);
274                                 }
275                                 else
276                                 {
277                                     aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
278                                     aNewPolygon.append(aNextPointOnXY);
279                                     aNewPolygon.append(aNextPointRotY);
280                                 }
281                             }
282                             else if(bLast)
283                             {
284                                 aNewPolygon.clear();
285 
286                                 if(bMiter)
287                                 {
288                                     aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
289                                     aNewPolygon.append(aCurrMiter);
290                                     aNewPolygon.append(aPointOnXY);
291 
292                                     aMiterPolygon.clear();
293                                     aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
294                                     aMiterPolygon.append(aPointRotY);
295                                     aMiterPolygon.append(aCurrMiter);
296                                 }
297                                 else
298                                 {
299                                     aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
300                                     aNewPolygon.append(aPointRotY);
301                                     aNewPolygon.append(aPointOnXY);
302                                 }
303                             }
304                             else
305                             {
306                                 aNewPolygon.clear();
307 
308                                 if(bMiter)
309                                 {
310                                     aNewPolygon.append(aPointOnXY);
311                                     aNewPolygon.append(aNextPointOnXY);
312                                     aNewPolygon.append(aNextMiter);
313                                     aNewPolygon.append(aCurrMiter);
314 
315                                     aMiterPolygon.clear();
316                                     aMiterPolygon.append(aCurrMiter);
317                                     aMiterPolygon.append(aNextMiter);
318                                     aMiterPolygon.append(aNextPointRotY);
319                                     aMiterPolygon.append(aPointRotY);
320                                 }
321                                 else
322                                 {
323                                     aNewPolygon.append(aPointRotY);
324                                     aNewPolygon.append(aPointOnXY);
325                                     aNewPolygon.append(aNextPointOnXY);
326                                     aNewPolygon.append(aNextPointRotY);
327                                 }
328                             }
329 
330                             // set normals
331                             for(sal_uInt32 b(0L); b < aNewPolygon.count(); b++)
332                             {
333                                 aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
334                             }
335 
336                             // create primitive
337                             if(aNewPolygon.count())
338                             {
339                                 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
340                                 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false);
341                                 aResultVector.push_back(pNew);
342                             }
343 
344                             if(bMiter && aMiterPolygon.count())
345                             {
346                                 // set normals
347                                 for(sal_uInt32 c(0L); c < aMiterPolygon.count(); c++)
348                                 {
349                                     aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c)));
350                                 }
351 
352                                 // create primitive
353                                 const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
354                                 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false);
355                                 aResultVector.push_back(pNew);
356                             }
357 
358                             // prepare next step
359                             if(bFirst || !bLast)
360                             {
361                                 aPointOnXY = aNextPointOnXY;
362                                 aPointRotY = aNextPointRotY;
363 
364                                 if(bMiter)
365                                 {
366                                     aCurrMiter = aNextMiter;
367                                 }
368                             }
369                         }
370                     }
371                 }
372 
373                 Primitive3DSequence aRetval(aResultVector.size());
374 
375                 for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
376                 {
377                     aRetval[a] = Primitive3DReference(aResultVector[a]);
378                 }
379 
380                 return aRetval;
381             }
382 
383             basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
384             {
385                 // build transformation from unit vector to vector
386                 basegfx::B3DHomMatrix aRetval;
387 
388                 // get applied rotations from angles in XY and in XZ (cartesian)
389                 const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength()));
390                 const double fRotInXZ(atan2(-rVector.getZ(), rVector.getX()));
391 
392                 // apply rotations. Rot around Z needs to be done first, so apply in two steps
393                 aRetval.rotate(0.0, 0.0, fRotInXY);
394                 aRetval.rotate(0.0, fRotInXZ, 0.0);
395 
396                 return aRetval;
397             }
398         } // end of anonymous namespace
399     } // end of namespace primitive3d
400 } // end of namespace drawinglayer
401 
402 //////////////////////////////////////////////////////////////////////////////
403 
404 using namespace com::sun::star;
405 
406 //////////////////////////////////////////////////////////////////////////////
407 
408 namespace drawinglayer
409 {
410     namespace primitive3d
411     {
412         Primitive3DSequence PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
413         {
414             const sal_uInt32 nPointCount(getB3DPolygon().count());
415             std::vector< BasePrimitive3D* > aResultVector;
416 
417             if(0L != nPointCount)
418             {
419                 if(basegfx::fTools::more(getRadius(), 0.0))
420                 {
421                     const attribute::MaterialAttribute3D aMaterial(getBColor());
422                     static sal_uInt32 nSegments(8L); // default for 3d line segments, for more quality just raise this value (in even steps)
423                     const bool bClosed(getB3DPolygon().isClosed());
424                     const bool bNoLineJoin(basegfx::B2DLINEJOIN_NONE == getLineJoin());
425                     const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1L);
426                     basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1L));
427                     basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0L));
428 
429                     for(sal_uInt32 a(0L); a < nLoopCount; a++)
430                     {
431                         // get next data
432                         const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1L) % nPointCount));
433                         const basegfx::B3DVector aForw(aNext - aCurr);
434                         const double fForwLen(aForw.getLength());
435 
436                         if(basegfx::fTools::more(fForwLen, 0.0))
437                         {
438                             // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
439                             basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
440 
441                             // create default transformation with scale and rotate
442                             basegfx::B3DHomMatrix aVectorTrans;
443                             aVectorTrans.scale(fForwLen, getRadius(), getRadius());
444                             aVectorTrans *= aRotVector;
445                             aVectorTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
446 
447                             if(bNoLineJoin || (!bClosed && !a))
448                             {
449                                 // line start edge, build transformed primitiveVector3D
450                                 TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aVectorTrans, getLineCapSegments(nSegments, aMaterial));
451                                 aResultVector.push_back(pNewTransformedA);
452                             }
453                             else
454                             {
455                                 const basegfx::B3DVector aBack(aCurr - aLast);
456                                 const double fCross(basegfx::cross(aBack, aForw).getLength());
457 
458                                 if(!basegfx::fTools::equalZero(fCross))
459                                 {
460                                     // line connect non-parallel, aBack, aForw, use getLineJoin()
461                                     const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. F_PI2
462                                     Primitive3DSequence aNewList(getLineJoinSegments(nSegments, aMaterial, fAngle, getDegreeStepWidth(), getMiterMinimumAngle(), getLineJoin()));
463 
464                                     // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
465                                     basegfx::B3DHomMatrix aInvRotVector(aRotVector);
466                                     aInvRotVector.invert();
467                                     basegfx::B3DVector aTransBack(aInvRotVector * aBack);
468                                     const double fRotInYZ(atan2(aTransBack.getY(), aTransBack.getZ()));
469 
470                                     // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
471                                     // Also apply usual scaling and translation
472                                     basegfx::B3DHomMatrix aSphereTrans;
473                                     aSphereTrans.rotate(0.0, F_PI2, 0.0);
474                                     aSphereTrans.rotate(F_PI - fRotInYZ, 0.0, 0.0);
475                                     aSphereTrans *= aRotVector;
476                                     aSphereTrans.scale(getRadius(), getRadius(), getRadius());
477                                     aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
478 
479                                     // line start edge, build transformed primitiveVector3D
480                                     TransformPrimitive3D* pNewTransformedB = new TransformPrimitive3D(aSphereTrans, aNewList);
481                                     aResultVector.push_back(pNewTransformedB);
482                                 }
483                             }
484 
485                             // create line segments, build transformed primitiveVector3D
486                             TransformPrimitive3D* pNewTransformedC = new TransformPrimitive3D(aVectorTrans, getLineTubeSegments(nSegments, aMaterial));
487                             aResultVector.push_back(pNewTransformedC);
488 
489                             if(bNoLineJoin || (!bClosed && ((a + 1L) == nLoopCount)))
490                             {
491                                 // line end edge, first rotate (mirror) and translate, then use use aRotVector
492                                 basegfx::B3DHomMatrix aBackTrans;
493                                 aBackTrans.rotate(0.0, F_PI, 0.0);
494                                 aBackTrans.translate(1.0, 0.0, 0.0);
495                                 aBackTrans.scale(fForwLen, getRadius(), getRadius());
496                                 aBackTrans *= aRotVector;
497                                 aBackTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
498 
499                                 // line end edge, build transformed primitiveVector3D
500                                 TransformPrimitive3D* pNewTransformedD = new TransformPrimitive3D(aBackTrans, getLineCapSegments(nSegments, aMaterial));
501                                 aResultVector.push_back(pNewTransformedD);
502                             }
503                         }
504 
505                         // prepare next loop step
506                         aLast = aCurr;
507                         aCurr = aNext;
508                     }
509                 }
510                 else
511                 {
512                     // create hairline
513                     PolygonHairlinePrimitive3D* pNew = new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor());
514                     aResultVector.push_back(pNew);
515                 }
516             }
517 
518             // prepare return value
519             Primitive3DSequence aRetval(aResultVector.size());
520 
521             for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
522             {
523                 aRetval[a] = Primitive3DReference(aResultVector[a]);
524             }
525 
526             return aRetval;
527         }
528 
529         PolygonTubePrimitive3D::PolygonTubePrimitive3D(
530             const basegfx::B3DPolygon& rPolygon,
531             const basegfx::BColor& rBColor,
532             double fRadius, basegfx::B2DLineJoin aLineJoin,
533             double fDegreeStepWidth,
534             double fMiterMinimumAngle)
535         :   PolygonHairlinePrimitive3D(rPolygon, rBColor),
536             maLast3DDecomposition(),
537             mfRadius(fRadius),
538             mfDegreeStepWidth(fDegreeStepWidth),
539             mfMiterMinimumAngle(fMiterMinimumAngle),
540             maLineJoin(aLineJoin)
541         {
542         }
543 
544         bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
545         {
546             if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
547             {
548                 const PolygonTubePrimitive3D& rCompare = (PolygonTubePrimitive3D&)rPrimitive;
549 
550                 return (getRadius() == rCompare.getRadius()
551                     && getDegreeStepWidth() == rCompare.getDegreeStepWidth()
552                     && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle()
553                     && getLineJoin() == rCompare.getLineJoin());
554             }
555 
556             return false;
557         }
558 
559         Primitive3DSequence PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
560         {
561             ::osl::MutexGuard aGuard( m_aMutex );
562 
563             if(!getLast3DDecomposition().hasElements())
564             {
565                 const Primitive3DSequence aNewSequence(impCreate3DDecomposition(rViewInformation));
566                 const_cast< PolygonTubePrimitive3D* >(this)->setLast3DDecomposition(aNewSequence);
567             }
568 
569             return getLast3DDecomposition();
570         }
571 
572         // provide unique ID
573         ImplPrimitrive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D)
574 
575     } // end of namespace primitive3d
576 } // end of namespace drawinglayer
577 
578 //////////////////////////////////////////////////////////////////////////////
579 // eof
580