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