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 		{
getLineTubeSegments(sal_uInt32 nSegments,const attribute::MaterialAttribute3D & rMaterial)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 
getLineCapSegments(sal_uInt32 nSegments,const attribute::MaterialAttribute3D & rMaterial)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 
getLineCapRoundSegments(sal_uInt32 nSegments,const attribute::MaterialAttribute3D & rMaterial)159             Primitive3DSequence getLineCapRoundSegments(
160                 sal_uInt32 nSegments,
161                 const attribute::MaterialAttribute3D& rMaterial)
162             {
163                 // static data for buffered tube primitives
164                 static Primitive3DSequence aLineCapRoundList;
165                 static sal_uInt32 nLineCapRoundSegments(0);
166                 static attribute::MaterialAttribute3D aLineMaterial;
167 
168                 // may exclusively change static data, use mutex
169                 ::osl::Mutex m_mutex;
170 
171                 if(nSegments != nLineCapRoundSegments || !(rMaterial == aLineMaterial))
172                 {
173                     nLineCapRoundSegments = nSegments;
174                     aLineMaterial = rMaterial;
175                     aLineCapRoundList = Primitive3DSequence();
176                 }
177 
178                 if(!aLineCapRoundList.hasElements() && nLineCapRoundSegments)
179                 {
180                     // calculate new horizontal segments
181                     sal_uInt32 nVerSeg(nSegments / 2);
182 
183                     if(nVerSeg < 1)
184                     {
185                         nVerSeg = 1;
186                     }
187 
188                     // create half-sphere; upper half of unit sphere
189                     basegfx::B3DPolyPolygon aSphere(
190                         basegfx::tools::createUnitSphereFillPolyPolygon(
191                             nSegments,
192                             nVerSeg,
193                             true,
194                             F_PI2, 0.0,
195                             0.0, F_2PI));
196                     const sal_uInt32 nCount(aSphere.count());
197 
198                     if(nCount)
199                     {
200                         // rotate to have sphere cap orientned to negative X-Axis; do not
201                         // forget to transform normals, too
202                         basegfx::B3DHomMatrix aSphereTrans;
203 
204                         aSphereTrans.rotate(0.0, 0.0, F_PI2);
205                         aSphere.transform(aSphereTrans);
206                         aSphere.transformNormals(aSphereTrans);
207 
208                         // realloc for primitives and create based on polygon snippets
209                         aLineCapRoundList.realloc(nCount);
210 
211                         for(sal_uInt32 a(0); a < nCount; a++)
212                         {
213                             const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a));
214                             const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
215 
216                             // need to create one primitive per Polygon since the primitive
217                             // is for planar PolyPolygons which is definitely not the case here
218                             aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D(
219                                 aPartPolyPolygon,
220                                 rMaterial,
221                                 false);
222                         }
223                     }
224                 }
225 
226                 return aLineCapRoundList;
227             }
228 
getLineJoinSegments(sal_uInt32 nSegments,const attribute::MaterialAttribute3D & rMaterial,double fAngle,double,double fMiterMinimumAngle,basegfx::B2DLineJoin aLineJoin)229 			Primitive3DSequence getLineJoinSegments(
230 				sal_uInt32 nSegments,
231 				const attribute::MaterialAttribute3D& rMaterial,
232 				double fAngle,
233 				double /*fDegreeStepWidth*/,
234 				double fMiterMinimumAngle,
235 				basegfx::B2DLineJoin aLineJoin)
236 			{
237 				// nSegments is for whole circle, adapt to half circle
238 				const sal_uInt32 nVerSeg(nSegments >> 1L);
239 				std::vector< BasePrimitive3D* > aResultVector;
240 
241 				if(nVerSeg)
242 				{
243 					if(basegfx::B2DLINEJOIN_ROUND == aLineJoin)
244 					{
245 						// calculate new horizontal segments
246 						const sal_uInt32 nHorSeg(basegfx::fround((fAngle / F_2PI) * (double)nSegments));
247 
248 						if(nHorSeg)
249 						{
250 							// create half-sphere
251 							const basegfx::B3DPolyPolygon aSphere(basegfx::tools::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle));
252 
253 							for(sal_uInt32 a(0L); a < aSphere.count(); a++)
254 							{
255 								const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a));
256 								const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
257 								BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false);
258 								aResultVector.push_back(pNew);
259 							}
260 						}
261 						else
262 						{
263 							// fallback to bevel when there is not at least one segment hor and ver
264 							aLineJoin = basegfx::B2DLINEJOIN_BEVEL;
265 						}
266 					}
267 
268 					if(basegfx::B2DLINEJOIN_MIDDLE == aLineJoin
269 						|| basegfx::B2DLINEJOIN_BEVEL == aLineJoin
270 						|| basegfx::B2DLINEJOIN_MITER == aLineJoin)
271 					{
272 						if(basegfx::B2DLINEJOIN_MITER == aLineJoin)
273 						{
274 							const double fMiterAngle(fAngle/2.0);
275 
276 							if(fMiterAngle < fMiterMinimumAngle)
277 							{
278 								// fallback to bevel when miter's angle is too small
279 								aLineJoin = basegfx::B2DLINEJOIN_BEVEL;
280 							}
281 						}
282 
283 						const double fInc(F_PI / (double)nVerSeg);
284 						const double fSin(sin(-fAngle));
285 						const double fCos(cos(-fAngle));
286 						const bool bMiter(basegfx::B2DLINEJOIN_MITER == aLineJoin);
287 						const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0);
288 						const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0);
289 						double fPos(-F_PI2);
290 						basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY;
291 						basegfx::B3DPoint aCurrMiter, aNextMiter;
292 						basegfx::B3DPolygon aNewPolygon, aMiterPolygon;
293 
294 						// close polygon
295 						aNewPolygon.setClosed(true);
296 						aMiterPolygon.setClosed(true);
297 
298 						for(sal_uInt32 a(0L); a < nVerSeg; a++)
299 						{
300 							const bool bFirst(0L == a);
301 							const bool bLast(a + 1L == nVerSeg);
302 
303 							if(bFirst || !bLast)
304 							{
305 								fPos += fInc;
306 
307 								aNextPointOnXY = basegfx::B3DPoint(
308 									cos(fPos),
309 									sin(fPos),
310 									0.0);
311 
312 								aNextPointRotY = basegfx::B3DPoint(
313 									aNextPointOnXY.getX() * fCos,
314 									aNextPointOnXY.getY(),
315 									aNextPointOnXY.getX() * fSin);
316 
317 								if(bMiter)
318 								{
319 									aNextMiter = basegfx::B3DPoint(
320 										aNextPointOnXY.getX(),
321 										aNextPointOnXY.getY(),
322 										fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
323 								}
324 							}
325 
326 							if(bFirst)
327 							{
328 								aNewPolygon.clear();
329 
330 								if(bMiter)
331 								{
332 									aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
333 									aNewPolygon.append(aNextPointOnXY);
334 									aNewPolygon.append(aNextMiter);
335 
336 									aMiterPolygon.clear();
337 									aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
338 									aMiterPolygon.append(aNextMiter);
339 									aMiterPolygon.append(aNextPointRotY);
340 								}
341 								else
342 								{
343 									aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
344 									aNewPolygon.append(aNextPointOnXY);
345 									aNewPolygon.append(aNextPointRotY);
346 								}
347 							}
348 							else if(bLast)
349 							{
350 								aNewPolygon.clear();
351 
352 								if(bMiter)
353 								{
354 									aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
355 									aNewPolygon.append(aCurrMiter);
356 									aNewPolygon.append(aPointOnXY);
357 
358 									aMiterPolygon.clear();
359 									aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
360 									aMiterPolygon.append(aPointRotY);
361 									aMiterPolygon.append(aCurrMiter);
362 								}
363 								else
364 								{
365 									aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
366 									aNewPolygon.append(aPointRotY);
367 									aNewPolygon.append(aPointOnXY);
368 								}
369 							}
370 							else
371 							{
372 								aNewPolygon.clear();
373 
374 								if(bMiter)
375 								{
376 									aNewPolygon.append(aPointOnXY);
377 									aNewPolygon.append(aNextPointOnXY);
378 									aNewPolygon.append(aNextMiter);
379 									aNewPolygon.append(aCurrMiter);
380 
381 									aMiterPolygon.clear();
382 									aMiterPolygon.append(aCurrMiter);
383 									aMiterPolygon.append(aNextMiter);
384 									aMiterPolygon.append(aNextPointRotY);
385 									aMiterPolygon.append(aPointRotY);
386 								}
387 								else
388 								{
389 									aNewPolygon.append(aPointRotY);
390 									aNewPolygon.append(aPointOnXY);
391 									aNewPolygon.append(aNextPointOnXY);
392 									aNewPolygon.append(aNextPointRotY);
393 								}
394 							}
395 
396 							// set normals
397 							for(sal_uInt32 b(0L); b < aNewPolygon.count(); b++)
398 							{
399 								aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
400 							}
401 
402 							// create primitive
403 							if(aNewPolygon.count())
404 							{
405 								const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
406 								BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false);
407 								aResultVector.push_back(pNew);
408 							}
409 
410 							if(bMiter && aMiterPolygon.count())
411 							{
412 								// set normals
413 								for(sal_uInt32 c(0L); c < aMiterPolygon.count(); c++)
414 								{
415 									aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c)));
416 								}
417 
418 								// create primitive
419 								const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
420 								BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false);
421 								aResultVector.push_back(pNew);
422 							}
423 
424 							// prepare next step
425 							if(bFirst || !bLast)
426 							{
427 								aPointOnXY = aNextPointOnXY;
428 								aPointRotY = aNextPointRotY;
429 
430 								if(bMiter)
431 								{
432 									aCurrMiter = aNextMiter;
433 								}
434 							}
435 						}
436 					}
437 				}
438 
439 				Primitive3DSequence aRetval(aResultVector.size());
440 
441 				for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
442 				{
443 					aRetval[a] = Primitive3DReference(aResultVector[a]);
444 				}
445 
446 				return aRetval;
447 			}
448 
getRotationFromVector(const basegfx::B3DVector & rVector)449 			basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
450 			{
451 				// build transformation from unit vector to vector
452 				basegfx::B3DHomMatrix aRetval;
453 
454 				// get applied rotations from angles in XY and in XZ (cartesian)
455 				const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength()));
456 				const double fRotInXZ(atan2(-rVector.getZ(), rVector.getX()));
457 
458 				// apply rotations. Rot around Z needs to be done first, so apply in two steps
459 				aRetval.rotate(0.0, 0.0, fRotInXY);
460 				aRetval.rotate(0.0, fRotInXZ, 0.0);
461 
462 				return aRetval;
463 			}
464 		} // end of anonymous namespace
465 	} // end of namespace primitive3d
466 } // end of namespace drawinglayer
467 
468 //////////////////////////////////////////////////////////////////////////////
469 
470 using namespace com::sun::star;
471 
472 //////////////////////////////////////////////////////////////////////////////
473 
474 namespace drawinglayer
475 {
476     namespace primitive3d
477     {
impCreate3DDecomposition(const geometry::ViewInformation3D &) const478         Primitive3DSequence PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
479         {
480             const sal_uInt32 nPointCount(getB3DPolygon().count());
481             std::vector< BasePrimitive3D* > aResultVector;
482 
483             if(nPointCount)
484             {
485                 if(basegfx::fTools::more(getRadius(), 0.0))
486                 {
487                     const attribute::MaterialAttribute3D aMaterial(getBColor());
488                     static sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps)
489                     const bool bClosed(getB3DPolygon().isClosed());
490                     const bool bNoLineJoin(basegfx::B2DLINEJOIN_NONE == getLineJoin());
491                     const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1);
492                     basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1));
493                     basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0));
494 
495                     for(sal_uInt32 a(0); a < nLoopCount; a++)
496                     {
497                         // get next data
498                         const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1) % nPointCount));
499                         const basegfx::B3DVector aForw(aNext - aCurr);
500                         const double fForwLen(aForw.getLength());
501 
502                         if(basegfx::fTools::more(fForwLen, 0.0))
503                         {
504                             // find out if linecap is active
505                             const bool bFirst(!a);
506                             const bool bLast(a + 1 == nLoopCount);
507                             const bool bLineCapPossible(!bClosed && (bFirst || bLast));
508                             const bool bLineCapRound(bLineCapPossible && com::sun::star::drawing::LineCap_ROUND == getLineCap());
509                             const bool bLineCapSquare(bLineCapPossible && com::sun::star::drawing::LineCap_SQUARE == getLineCap());
510 
511                             // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
512                             basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
513 
514                             // prepare transformations for tube and cap
515                             basegfx::B3DHomMatrix aTubeTrans;
516                             basegfx::B3DHomMatrix aCapTrans;
517 
518                             // cap gets radius size
519                             aCapTrans.scale(getRadius(), getRadius(), getRadius());
520 
521                             if(bLineCapSquare)
522                             {
523                                 // when square line cap just prolong line segment in X, maybe 2 x radius when
524                                 // first and last (simple line segment)
525                                 const double fExtraLength(bFirst && bLast ? getRadius() * 2.0 : getRadius());
526 
527                                 aTubeTrans.scale(fForwLen + fExtraLength, getRadius(), getRadius());
528 
529                                 if(bFirst)
530                                 {
531                                     // correct start positions for tube and cap when first and square prolonged
532                                     aTubeTrans.translate(-getRadius(), 0.0, 0.0);
533                                     aCapTrans.translate(-getRadius(), 0.0, 0.0);
534                                 }
535                             }
536                             else
537                             {
538                                 // normal tube size
539                                 aTubeTrans.scale(fForwLen, getRadius(), getRadius());
540                             }
541 
542                             // rotate and translate tube and cap
543                             aTubeTrans *= aRotVector;
544                             aTubeTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
545                             aCapTrans *= aRotVector;
546                             aCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
547 
548                             if(bNoLineJoin || (!bClosed && bFirst))
549                             {
550                                 // line start edge, build transformed primitiveVector3D
551                                 Primitive3DSequence aSequence;
552 
553                                 if(bLineCapRound && bFirst)
554                                 {
555                                     // LineCapRound used
556                                     aSequence = getLineCapRoundSegments(nSegments, aMaterial);
557                                 }
558                                 else
559                                 {
560                                     // simple closing cap
561                                     aSequence = getLineCapSegments(nSegments, aMaterial);
562                                 }
563 
564                                 TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aCapTrans, aSequence);
565                                 aResultVector.push_back(pNewTransformedA);
566                             }
567                             else
568                             {
569                                 const basegfx::B3DVector aBack(aCurr - aLast);
570                                 const double fCross(basegfx::cross(aBack, aForw).getLength());
571 
572                                 if(!basegfx::fTools::equalZero(fCross))
573                                 {
574                                     // line connect non-parallel, aBack, aForw, use getLineJoin()
575                                     const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. F_PI2
576                                     Primitive3DSequence aNewList(
577                                         getLineJoinSegments(
578                                             nSegments,
579                                             aMaterial,
580                                             fAngle,
581                                             getDegreeStepWidth(),
582                                             getMiterMinimumAngle(),
583                                             getLineJoin()));
584 
585                                     // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
586                                     basegfx::B3DHomMatrix aInvRotVector(aRotVector);
587                                     aInvRotVector.invert();
588                                     basegfx::B3DVector aTransBack(aInvRotVector * aBack);
589                                     const double fRotInYZ(atan2(aTransBack.getY(), aTransBack.getZ()));
590 
591                                     // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
592                                     // Also apply usual scaling and translation
593                                     basegfx::B3DHomMatrix aSphereTrans;
594                                     aSphereTrans.rotate(0.0, F_PI2, 0.0);
595                                     aSphereTrans.rotate(F_PI - fRotInYZ, 0.0, 0.0);
596                                     aSphereTrans *= aRotVector;
597                                     aSphereTrans.scale(getRadius(), getRadius(), getRadius());
598                                     aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
599 
600                                     // line start edge, build transformed primitiveVector3D
601                                     aResultVector.push_back(
602                                         new TransformPrimitive3D(
603                                             aSphereTrans,
604                                             aNewList));
605                                 }
606                             }
607 
608                             // create line segments, build transformed primitiveVector3D
609                             aResultVector.push_back(
610                                 new TransformPrimitive3D(
611                                     aTubeTrans,
612                                     getLineTubeSegments(nSegments, aMaterial)));
613 
614                             if(bNoLineJoin || (!bClosed && bLast))
615                             {
616                                 // line end edge
617                                 basegfx::B3DHomMatrix aBackCapTrans;
618 
619                                 // Mirror (line end) and radius scale
620                                 aBackCapTrans.rotate(0.0, F_PI, 0.0);
621                                 aBackCapTrans.scale(getRadius(), getRadius(), getRadius());
622 
623                                 if(bLineCapSquare && bLast)
624                                 {
625                                     // correct position when square and prolonged
626                                     aBackCapTrans.translate(fForwLen + getRadius(), 0.0, 0.0);
627                                 }
628                                 else
629                                 {
630                                     // standard position
631                                     aBackCapTrans.translate(fForwLen, 0.0, 0.0);
632                                 }
633 
634                                 // rotate and translate to destination
635                                 aBackCapTrans *= aRotVector;
636                                 aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
637 
638                                 // get primitiveVector3D
639                                 Primitive3DSequence aSequence;
640 
641                                 if(bLineCapRound && bLast)
642                                 {
643                                     // LineCapRound used
644                                     aSequence = getLineCapRoundSegments(nSegments, aMaterial);
645                                 }
646                                 else
647                                 {
648                                     // simple closing cap
649                                     aSequence = getLineCapSegments(nSegments, aMaterial);
650                                 }
651 
652                                 aResultVector.push_back(
653                                     new TransformPrimitive3D(
654                                         aBackCapTrans,
655                                         aSequence));
656                             }
657                         }
658 
659 						// prepare next loop step
660 						aLast = aCurr;
661 						aCurr = aNext;
662 					}
663 				}
664 				else
665 				{
666 					// create hairline
667 					PolygonHairlinePrimitive3D* pNew = new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor());
668 					aResultVector.push_back(pNew);
669 				}
670 			}
671 
672 			// prepare return value
673 			Primitive3DSequence aRetval(aResultVector.size());
674 
675 			for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
676 			{
677 				aRetval[a] = Primitive3DReference(aResultVector[a]);
678 			}
679 
680 			return aRetval;
681 		}
682 
PolygonTubePrimitive3D(const basegfx::B3DPolygon & rPolygon,const basegfx::BColor & rBColor,double fRadius,basegfx::B2DLineJoin aLineJoin,com::sun::star::drawing::LineCap aLineCap,double fDegreeStepWidth,double fMiterMinimumAngle)683 		PolygonTubePrimitive3D::PolygonTubePrimitive3D(
684 			const basegfx::B3DPolygon& rPolygon,
685 			const basegfx::BColor& rBColor,
686 			double fRadius, basegfx::B2DLineJoin aLineJoin,
687             com::sun::star::drawing::LineCap aLineCap,
688 			double fDegreeStepWidth,
689 			double fMiterMinimumAngle)
690 		:	PolygonHairlinePrimitive3D(rPolygon, rBColor),
691 			maLast3DDecomposition(),
692             mfRadius(fRadius),
693 			mfDegreeStepWidth(fDegreeStepWidth),
694 			mfMiterMinimumAngle(fMiterMinimumAngle),
695 			maLineJoin(aLineJoin),
696             maLineCap(aLineCap)
697 		{
698 		}
699 
operator ==(const BasePrimitive3D & rPrimitive) const700 		bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
701 		{
702 			if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
703 			{
704 				const PolygonTubePrimitive3D& rCompare = (PolygonTubePrimitive3D&)rPrimitive;
705 
706 				return (getRadius() == rCompare.getRadius()
707 					&& getDegreeStepWidth() == rCompare.getDegreeStepWidth()
708 					&& getMiterMinimumAngle() == rCompare.getMiterMinimumAngle()
709 					&& getLineJoin() == rCompare.getLineJoin()
710                     && getLineCap() == rCompare.getLineCap());
711 			}
712 
713 			return false;
714 		}
715 
get3DDecomposition(const geometry::ViewInformation3D & rViewInformation) const716 		Primitive3DSequence PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
717 		{
718 			::osl::MutexGuard aGuard( m_aMutex );
719 
720 			if(!getLast3DDecomposition().hasElements())
721 			{
722 				const Primitive3DSequence aNewSequence(impCreate3DDecomposition(rViewInformation));
723 				const_cast< PolygonTubePrimitive3D* >(this)->setLast3DDecomposition(aNewSequence);
724 			}
725 
726 			return getLast3DDecomposition();
727 		}
728 
729         // provide unique ID
730 		ImplPrimitrive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D)
731 
732 	} // end of namespace primitive3d
733 } // end of namespace drawinglayer
734 
735 //////////////////////////////////////////////////////////////////////////////
736 // eof
737