xref: /AOO41X/main/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx (revision 5aaf853b3ba91aa8a4f8154519fb0bf086e1a428)
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