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