1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawinglayer/primitive3d/polygontubeprimitive3d.hxx>
21 #include <drawinglayer/attribute/materialattribute3d.hxx>
22 #include <basegfx/matrix/b3dhommatrix.hxx>
23 #include <basegfx/polygon/b3dpolypolygon.hxx>
24 #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
25 #include <basegfx/polygon/b3dpolypolygontools.hxx>
26 #include <drawinglayer/primitive3d/transformprimitive3d.hxx>
27 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
28 #include <rtl/instance.hxx>
31 namespace drawinglayer
35 namespace // anonymous namespace
40 // data for buffered tube primitives
41 Primitive3DContainer m_aLineTubeList
;
42 sal_uInt32 m_nLineTubeSegments
;
43 attribute::MaterialAttribute3D m_aLineMaterial
;
44 ::osl::Mutex m_aMutex
;
47 : m_nLineTubeSegments(0)
51 TubeBuffer(const TubeBuffer
&) = delete;
52 const TubeBuffer
& operator=(const TubeBuffer
&) = delete;
54 Primitive3DContainer
getLineTubeSegments(
56 const attribute::MaterialAttribute3D
& rMaterial
)
58 // may exclusively change cached data, use mutex
59 ::osl::MutexGuard
aGuard(m_aMutex
);
61 if (nSegments
!= m_nLineTubeSegments
|| !(rMaterial
== m_aLineMaterial
))
63 m_nLineTubeSegments
= nSegments
;
64 m_aLineMaterial
= rMaterial
;
65 m_aLineTubeList
= Primitive3DContainer();
68 if (m_aLineTubeList
.empty() && m_nLineTubeSegments
!= 0)
70 const basegfx::B3DPoint
aLeft(0.0, 0.0, 0.0);
71 const basegfx::B3DPoint
aRight(1.0, 0.0, 0.0);
72 basegfx::B3DPoint
aLastLeft(0.0, 1.0, 0.0);
73 basegfx::B3DPoint
aLastRight(1.0, 1.0, 0.0);
74 basegfx::B3DHomMatrix aRot
;
75 aRot
.rotate(F_2PI
/ static_cast<double>(m_nLineTubeSegments
), 0.0, 0.0);
76 m_aLineTubeList
.resize(m_nLineTubeSegments
);
78 for(sal_uInt32 a
= 0; a
< m_nLineTubeSegments
; ++a
)
80 const basegfx::B3DPoint
aNextLeft(aRot
* aLastLeft
);
81 const basegfx::B3DPoint
aNextRight(aRot
* aLastRight
);
82 basegfx::B3DPolygon aNewPolygon
;
84 aNewPolygon
.append(aNextLeft
);
85 aNewPolygon
.setNormal(0, basegfx::B3DVector(aNextLeft
- aLeft
));
87 aNewPolygon
.append(aLastLeft
);
88 aNewPolygon
.setNormal(1, basegfx::B3DVector(aLastLeft
- aLeft
));
90 aNewPolygon
.append(aLastRight
);
91 aNewPolygon
.setNormal(2, basegfx::B3DVector(aLastRight
- aRight
));
93 aNewPolygon
.append(aNextRight
);
94 aNewPolygon
.setNormal(3, basegfx::B3DVector(aNextRight
- aRight
));
96 aNewPolygon
.setClosed(true);
98 const basegfx::B3DPolyPolygon
aNewPolyPolygon(aNewPolygon
);
99 const Primitive3DReference
xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon
, m_aLineMaterial
, false));
100 m_aLineTubeList
[a
] = xRef
;
102 aLastLeft
= aNextLeft
;
103 aLastRight
= aNextRight
;
106 return m_aLineTubeList
;
110 struct theTubeBuffer
:
111 public rtl::Static
< TubeBuffer
, theTubeBuffer
> {};
113 Primitive3DContainer
getLineTubeSegments(
114 sal_uInt32 nSegments
,
115 const attribute::MaterialAttribute3D
& rMaterial
)
117 // static data for buffered tube primitives
118 TubeBuffer
&rTheBuffer
= theTubeBuffer::get();
119 return rTheBuffer
.getLineTubeSegments(nSegments
, rMaterial
);
125 // data for buffered cap primitives
126 Primitive3DContainer m_aLineCapList
;
127 sal_uInt32 m_nLineCapSegments
;
128 attribute::MaterialAttribute3D m_aLineMaterial
;
129 ::osl::Mutex m_aMutex
;
132 : m_nLineCapSegments(0)
135 CapBuffer(const CapBuffer
&) = delete;
136 const CapBuffer
& operator=(const CapBuffer
&) = delete;
138 Primitive3DContainer
getLineCapSegments(
139 sal_uInt32 nSegments
,
140 const attribute::MaterialAttribute3D
& rMaterial
)
142 // may exclusively change cached data, use mutex
143 ::osl::MutexGuard
aGuard(m_aMutex
);
145 if (nSegments
!= m_nLineCapSegments
|| !(rMaterial
== m_aLineMaterial
))
147 m_nLineCapSegments
= nSegments
;
148 m_aLineMaterial
= rMaterial
;
149 m_aLineCapList
= Primitive3DContainer();
152 if (m_aLineCapList
.empty() && m_nLineCapSegments
!= 0)
154 const basegfx::B3DPoint
aNull(0.0, 0.0, 0.0);
155 basegfx::B3DPoint
aLast(0.0, 1.0, 0.0);
156 basegfx::B3DHomMatrix aRot
;
157 aRot
.rotate(F_2PI
/ static_cast<double>(m_nLineCapSegments
), 0.0, 0.0);
158 m_aLineCapList
.resize(m_nLineCapSegments
);
160 for(sal_uInt32 a
= 0; a
< m_nLineCapSegments
; ++a
)
162 const basegfx::B3DPoint
aNext(aRot
* aLast
);
163 basegfx::B3DPolygon aNewPolygon
;
165 aNewPolygon
.append(aLast
);
166 aNewPolygon
.setNormal(0, basegfx::B3DVector(aLast
- aNull
));
168 aNewPolygon
.append(aNext
);
169 aNewPolygon
.setNormal(1, basegfx::B3DVector(aNext
- aNull
));
171 aNewPolygon
.append(aNull
);
172 aNewPolygon
.setNormal(2, basegfx::B3DVector(-1.0, 0.0, 0.0));
174 aNewPolygon
.setClosed(true);
176 const basegfx::B3DPolyPolygon
aNewPolyPolygon(aNewPolygon
);
177 const Primitive3DReference
xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon
, m_aLineMaterial
, false));
178 m_aLineCapList
[a
] = xRef
;
184 return m_aLineCapList
;
188 struct theCapBuffer
:
189 public rtl::Static
< CapBuffer
, theCapBuffer
> {};
191 Primitive3DContainer
getLineCapSegments(
192 sal_uInt32 nSegments
,
193 const attribute::MaterialAttribute3D
& rMaterial
)
195 // static data for buffered cap primitives
196 CapBuffer
&rTheBuffer
= theCapBuffer::get();
197 return rTheBuffer
.getLineCapSegments(nSegments
, rMaterial
);
203 // data for buffered capround primitives
204 Primitive3DContainer m_aLineCapRoundList
;
205 sal_uInt32 m_nLineCapRoundSegments
;
206 attribute::MaterialAttribute3D m_aLineMaterial
;
207 ::osl::Mutex m_aMutex
;
210 : m_nLineCapRoundSegments(0)
213 CapRoundBuffer(const CapRoundBuffer
&) = delete;
214 const CapRoundBuffer
& operator=(const CapRoundBuffer
&) = delete;
216 Primitive3DContainer
getLineCapRoundSegments(
217 sal_uInt32 nSegments
,
218 const attribute::MaterialAttribute3D
& rMaterial
)
220 // may exclusively change cached data, use mutex
221 ::osl::MutexGuard
aGuard(m_aMutex
);
223 if (nSegments
!= m_nLineCapRoundSegments
|| !(rMaterial
== m_aLineMaterial
))
225 m_nLineCapRoundSegments
= nSegments
;
226 m_aLineMaterial
= rMaterial
;
227 m_aLineCapRoundList
= Primitive3DContainer();
230 if (m_aLineCapRoundList
.empty() && m_nLineCapRoundSegments
)
232 // calculate new horizontal segments
233 sal_uInt32
nVerSeg(nSegments
/ 2);
240 // create half-sphere; upper half of unit sphere
241 basegfx::B3DPolyPolygon
aSphere(
242 basegfx::utils::createUnitSphereFillPolyPolygon(
248 const sal_uInt32
nCount(aSphere
.count());
252 // rotate to have sphere cap oriented to negative X-Axis; do not
253 // forget to transform normals, too
254 basegfx::B3DHomMatrix aSphereTrans
;
256 aSphereTrans
.rotate(0.0, 0.0, F_PI2
);
257 aSphere
.transform(aSphereTrans
);
258 aSphere
.transformNormals(aSphereTrans
);
260 // realloc for primitives and create based on polygon snippets
261 m_aLineCapRoundList
.resize(nCount
);
263 for (sal_uInt32 a
= 0; a
< nCount
; ++a
)
265 const basegfx::B3DPolygon
& aPartPolygon(aSphere
.getB3DPolygon(a
));
266 const basegfx::B3DPolyPolygon
aPartPolyPolygon(aPartPolygon
);
268 // need to create one primitive per Polygon since the primitive
269 // is for planar PolyPolygons which is definitely not the case here
270 m_aLineCapRoundList
[a
] = new PolyPolygonMaterialPrimitive3D(
278 return m_aLineCapRoundList
;
283 struct theCapRoundBuffer
:
284 public rtl::Static
< CapRoundBuffer
, theCapRoundBuffer
> {};
287 Primitive3DContainer
getLineCapRoundSegments(
288 sal_uInt32 nSegments
,
289 const attribute::MaterialAttribute3D
& rMaterial
)
291 // static data for buffered cap primitives
292 CapRoundBuffer
&rTheBuffer
= theCapRoundBuffer::get();
293 return rTheBuffer
.getLineCapRoundSegments(nSegments
, rMaterial
);
296 Primitive3DContainer
getLineJoinSegments(
297 sal_uInt32 nSegments
,
298 const attribute::MaterialAttribute3D
& rMaterial
,
300 double fMiterMinimumAngle
,
301 basegfx::B2DLineJoin aLineJoin
)
303 // nSegments is for whole circle, adapt to half circle
304 const sal_uInt32
nVerSeg(nSegments
>> 1);
305 std::vector
< BasePrimitive3D
* > aResultVector
;
309 if(basegfx::B2DLineJoin::Round
== aLineJoin
)
311 // calculate new horizontal segments
312 const sal_uInt32
nHorSeg(basegfx::fround((fAngle
/ F_2PI
) * static_cast<double>(nSegments
)));
316 // create half-sphere
317 const basegfx::B3DPolyPolygon
aSphere(basegfx::utils::createUnitSphereFillPolyPolygon(nHorSeg
, nVerSeg
, true, F_PI2
, -F_PI2
, 0.0, fAngle
));
319 for(sal_uInt32
a(0); a
< aSphere
.count(); a
++)
321 const basegfx::B3DPolygon
& aPartPolygon(aSphere
.getB3DPolygon(a
));
322 const basegfx::B3DPolyPolygon
aPartPolyPolygon(aPartPolygon
);
323 BasePrimitive3D
* pNew
= new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon
, rMaterial
, false);
324 aResultVector
.push_back(pNew
);
329 // fallback to bevel when there is not at least one segment hor and ver
330 aLineJoin
= basegfx::B2DLineJoin::Bevel
;
334 if (basegfx::B2DLineJoin::Bevel
== aLineJoin
||
335 basegfx::B2DLineJoin::Miter
== aLineJoin
)
337 if(basegfx::B2DLineJoin::Miter
== aLineJoin
)
339 const double fMiterAngle(fAngle
/2.0);
341 if(fMiterAngle
< fMiterMinimumAngle
)
343 // fallback to bevel when miter's angle is too small
344 aLineJoin
= basegfx::B2DLineJoin::Bevel
;
348 const double fInc(F_PI
/ static_cast<double>(nVerSeg
));
349 const double fSin(sin(-fAngle
));
350 const double fCos(cos(-fAngle
));
351 const bool bMiter(basegfx::B2DLineJoin::Miter
== aLineJoin
);
352 const double fMiterSin(bMiter
? sin(-(fAngle
/2.0)) : 0.0);
353 const double fMiterCos(bMiter
? cos(-(fAngle
/2.0)) : 0.0);
355 basegfx::B3DPoint aPointOnXY
, aPointRotY
, aNextPointOnXY
, aNextPointRotY
;
356 basegfx::B3DPoint aCurrMiter
, aNextMiter
;
357 basegfx::B3DPolygon aNewPolygon
, aMiterPolygon
;
360 aNewPolygon
.setClosed(true);
361 aMiterPolygon
.setClosed(true);
363 for(sal_uInt32
a(0); a
< nVerSeg
; a
++)
365 const bool bFirst(0 == a
);
366 const bool bLast(a
+ 1 == nVerSeg
);
372 aNextPointOnXY
= basegfx::B3DPoint(
377 aNextPointRotY
= basegfx::B3DPoint(
378 aNextPointOnXY
.getX() * fCos
,
379 aNextPointOnXY
.getY(),
380 aNextPointOnXY
.getX() * fSin
);
384 aNextMiter
= basegfx::B3DPoint(
385 aNextPointOnXY
.getX(),
386 aNextPointOnXY
.getY(),
387 fMiterSin
* (aNextPointOnXY
.getX() / fMiterCos
));
397 aNewPolygon
.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
398 aNewPolygon
.append(aNextPointOnXY
);
399 aNewPolygon
.append(aNextMiter
);
401 aMiterPolygon
.clear();
402 aMiterPolygon
.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
403 aMiterPolygon
.append(aNextMiter
);
404 aMiterPolygon
.append(aNextPointRotY
);
408 aNewPolygon
.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
409 aNewPolygon
.append(aNextPointOnXY
);
410 aNewPolygon
.append(aNextPointRotY
);
419 aNewPolygon
.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
420 aNewPolygon
.append(aCurrMiter
);
421 aNewPolygon
.append(aPointOnXY
);
423 aMiterPolygon
.clear();
424 aMiterPolygon
.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
425 aMiterPolygon
.append(aPointRotY
);
426 aMiterPolygon
.append(aCurrMiter
);
430 aNewPolygon
.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
431 aNewPolygon
.append(aPointRotY
);
432 aNewPolygon
.append(aPointOnXY
);
441 aNewPolygon
.append(aPointOnXY
);
442 aNewPolygon
.append(aNextPointOnXY
);
443 aNewPolygon
.append(aNextMiter
);
444 aNewPolygon
.append(aCurrMiter
);
446 aMiterPolygon
.clear();
447 aMiterPolygon
.append(aCurrMiter
);
448 aMiterPolygon
.append(aNextMiter
);
449 aMiterPolygon
.append(aNextPointRotY
);
450 aMiterPolygon
.append(aPointRotY
);
454 aNewPolygon
.append(aPointRotY
);
455 aNewPolygon
.append(aPointOnXY
);
456 aNewPolygon
.append(aNextPointOnXY
);
457 aNewPolygon
.append(aNextPointRotY
);
462 for(sal_uInt32
b(0); b
< aNewPolygon
.count(); b
++)
464 aNewPolygon
.setNormal(b
, basegfx::B3DVector(aNewPolygon
.getB3DPoint(b
)));
468 if(aNewPolygon
.count())
470 const basegfx::B3DPolyPolygon
aNewPolyPolygon(aNewPolygon
);
471 BasePrimitive3D
* pNew
= new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon
, rMaterial
, false);
472 aResultVector
.push_back(pNew
);
475 if(bMiter
&& aMiterPolygon
.count())
478 for(sal_uInt32
c(0); c
< aMiterPolygon
.count(); c
++)
480 aMiterPolygon
.setNormal(c
, basegfx::B3DVector(aMiterPolygon
.getB3DPoint(c
)));
484 const basegfx::B3DPolyPolygon
aMiterPolyPolygon(aMiterPolygon
);
485 BasePrimitive3D
* pNew
= new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon
, rMaterial
, false);
486 aResultVector
.push_back(pNew
);
492 aPointOnXY
= aNextPointOnXY
;
493 aPointRotY
= aNextPointRotY
;
497 aCurrMiter
= aNextMiter
;
504 Primitive3DContainer
aRetval(aResultVector
.size());
506 for(size_t a(0); a
< aResultVector
.size(); a
++)
508 aRetval
[a
] = Primitive3DReference(aResultVector
[a
]);
514 basegfx::B3DHomMatrix
getRotationFromVector(const basegfx::B3DVector
& rVector
)
516 // build transformation from unit vector to vector
517 basegfx::B3DHomMatrix aRetval
;
519 // get applied rotations from angles in XY and in XZ (cartesian)
520 const double fRotInXY(atan2(rVector
.getY(), rVector
.getXZLength()));
521 const double fRotInXZ(atan2(-rVector
.getZ(), rVector
.getX()));
523 // apply rotations. Rot around Z needs to be done first, so apply in two steps
524 aRetval
.rotate(0.0, 0.0, fRotInXY
);
525 aRetval
.rotate(0.0, fRotInXZ
, 0.0);
529 } // end of anonymous namespace
530 } // end of namespace primitive3d
531 } // end of namespace drawinglayer
534 using namespace com::sun::star
;
537 namespace drawinglayer
539 namespace primitive3d
541 Primitive3DContainer
PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D
& /*rViewInformation*/) const
543 const sal_uInt32
nPointCount(getB3DPolygon().count());
544 std::vector
< BasePrimitive3D
* > aResultVector
;
548 if(basegfx::fTools::more(getRadius(), 0.0))
550 const attribute::MaterialAttribute3D
aMaterial(getBColor());
551 static const sal_uInt32
nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps)
552 const bool bClosed(getB3DPolygon().isClosed());
553 const bool bNoLineJoin(basegfx::B2DLineJoin::NONE
== getLineJoin());
554 const sal_uInt32
nLoopCount(bClosed
? nPointCount
: nPointCount
- 1);
555 basegfx::B3DPoint
aLast(getB3DPolygon().getB3DPoint(nPointCount
- 1));
556 basegfx::B3DPoint
aCurr(getB3DPolygon().getB3DPoint(0));
558 for(sal_uInt32
a(0); a
< nLoopCount
; a
++)
561 const basegfx::B3DPoint
aNext(getB3DPolygon().getB3DPoint((a
+ 1) % nPointCount
));
562 const basegfx::B3DVector
aForw(aNext
- aCurr
);
563 const double fForwLen(aForw
.getLength());
565 if(basegfx::fTools::more(fForwLen
, 0.0))
567 // find out if linecap is active
568 const bool bFirst(!a
);
569 const bool bLast(a
+ 1 == nLoopCount
);
570 const bool bLineCapPossible(!bClosed
&& (bFirst
|| bLast
));
571 const bool bLineCapRound(bLineCapPossible
&& css::drawing::LineCap_ROUND
== getLineCap());
572 const bool bLineCapSquare(bLineCapPossible
&& css::drawing::LineCap_SQUARE
== getLineCap());
574 // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
575 basegfx::B3DHomMatrix
aRotVector(getRotationFromVector(aForw
));
577 // prepare transformations for tube and cap
578 basegfx::B3DHomMatrix aTubeTrans
;
579 basegfx::B3DHomMatrix aCapTrans
;
581 // cap gets radius size
582 aCapTrans
.scale(getRadius(), getRadius(), getRadius());
586 // when square line cap just prolong line segment in X, maybe 2 x radius when
587 // first and last (simple line segment)
588 const double fExtraLength(bFirst
&& bLast
? getRadius() * 2.0 : getRadius());
590 aTubeTrans
.scale(fForwLen
+ fExtraLength
, getRadius(), getRadius());
594 // correct start positions for tube and cap when first and square prolonged
595 aTubeTrans
.translate(-getRadius(), 0.0, 0.0);
596 aCapTrans
.translate(-getRadius(), 0.0, 0.0);
602 aTubeTrans
.scale(fForwLen
, getRadius(), getRadius());
605 // rotate and translate tube and cap
606 aTubeTrans
*= aRotVector
;
607 aTubeTrans
.translate(aCurr
.getX(), aCurr
.getY(), aCurr
.getZ());
608 aCapTrans
*= aRotVector
;
609 aCapTrans
.translate(aCurr
.getX(), aCurr
.getY(), aCurr
.getZ());
611 if(bNoLineJoin
|| (!bClosed
&& bFirst
))
613 // line start edge, build transformed primitiveVector3D
614 Primitive3DContainer aSequence
;
616 if(bLineCapRound
&& bFirst
)
619 aSequence
= getLineCapRoundSegments(nSegments
, aMaterial
);
623 // simple closing cap
624 aSequence
= getLineCapSegments(nSegments
, aMaterial
);
627 TransformPrimitive3D
* pNewTransformedA
= new TransformPrimitive3D(aCapTrans
, aSequence
);
628 aResultVector
.push_back(pNewTransformedA
);
632 const basegfx::B3DVector
aBack(aCurr
- aLast
);
633 const double fCross(basegfx::cross(aBack
, aForw
).getLength());
635 if(!basegfx::fTools::equalZero(fCross
))
637 // line connect non-parallel, aBack, aForw, use getLineJoin()
638 const double fAngle(acos(aBack
.scalar(aForw
) / (fForwLen
* aBack
.getLength()))); // 0.0 .. F_PI2
639 Primitive3DContainer
aNewList(
644 getMiterMinimumAngle(),
647 // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
648 basegfx::B3DHomMatrix
aInvRotVector(aRotVector
);
649 aInvRotVector
.invert();
650 basegfx::B3DVector
aTransBack(aInvRotVector
* aBack
);
651 const double fRotInYZ(atan2(aTransBack
.getY(), aTransBack
.getZ()));
653 // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
654 // Also apply usual scaling and translation
655 basegfx::B3DHomMatrix aSphereTrans
;
656 aSphereTrans
.rotate(0.0, F_PI2
, 0.0);
657 aSphereTrans
.rotate(F_PI
- fRotInYZ
, 0.0, 0.0);
658 aSphereTrans
*= aRotVector
;
659 aSphereTrans
.scale(getRadius(), getRadius(), getRadius());
660 aSphereTrans
.translate(aCurr
.getX(), aCurr
.getY(), aCurr
.getZ());
662 // line start edge, build transformed primitiveVector3D
663 aResultVector
.push_back(
664 new TransformPrimitive3D(
670 // create line segments, build transformed primitiveVector3D
671 aResultVector
.push_back(
672 new TransformPrimitive3D(
674 getLineTubeSegments(nSegments
, aMaterial
)));
676 if(bNoLineJoin
|| (!bClosed
&& bLast
))
679 basegfx::B3DHomMatrix aBackCapTrans
;
681 // Mirror (line end) and radius scale
682 aBackCapTrans
.rotate(0.0, F_PI
, 0.0);
683 aBackCapTrans
.scale(getRadius(), getRadius(), getRadius());
685 if(bLineCapSquare
&& bLast
)
687 // correct position when square and prolonged
688 aBackCapTrans
.translate(fForwLen
+ getRadius(), 0.0, 0.0);
693 aBackCapTrans
.translate(fForwLen
, 0.0, 0.0);
696 // rotate and translate to destination
697 aBackCapTrans
*= aRotVector
;
698 aBackCapTrans
.translate(aCurr
.getX(), aCurr
.getY(), aCurr
.getZ());
700 // get primitiveVector3D
701 Primitive3DContainer aSequence
;
703 if(bLineCapRound
&& bLast
)
706 aSequence
= getLineCapRoundSegments(nSegments
, aMaterial
);
710 // simple closing cap
711 aSequence
= getLineCapSegments(nSegments
, aMaterial
);
714 aResultVector
.push_back(
715 new TransformPrimitive3D(
721 // prepare next loop step
729 PolygonHairlinePrimitive3D
* pNew
= new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor());
730 aResultVector
.push_back(pNew
);
734 // prepare return value
735 Primitive3DContainer
aRetval(aResultVector
.size());
737 for(size_t a(0); a
< aResultVector
.size(); a
++)
739 aRetval
[a
] = Primitive3DReference(aResultVector
[a
]);
745 PolygonTubePrimitive3D::PolygonTubePrimitive3D(
746 const basegfx::B3DPolygon
& rPolygon
,
747 const basegfx::BColor
& rBColor
,
748 double fRadius
, basegfx::B2DLineJoin aLineJoin
,
749 css::drawing::LineCap aLineCap
,
750 double fDegreeStepWidth
,
751 double fMiterMinimumAngle
)
752 : PolygonHairlinePrimitive3D(rPolygon
, rBColor
),
753 maLast3DDecomposition(),
755 mfDegreeStepWidth(fDegreeStepWidth
),
756 mfMiterMinimumAngle(fMiterMinimumAngle
),
757 maLineJoin(aLineJoin
),
762 bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D
& rPrimitive
) const
764 if(PolygonHairlinePrimitive3D::operator==(rPrimitive
))
766 const PolygonTubePrimitive3D
& rCompare
= static_cast<const PolygonTubePrimitive3D
&>(rPrimitive
);
768 return (getRadius() == rCompare
.getRadius()
769 && getDegreeStepWidth() == rCompare
.getDegreeStepWidth()
770 && getMiterMinimumAngle() == rCompare
.getMiterMinimumAngle()
771 && getLineJoin() == rCompare
.getLineJoin()
772 && getLineCap() == rCompare
.getLineCap());
778 Primitive3DContainer
PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D
& rViewInformation
) const
780 ::osl::MutexGuard
aGuard( m_aMutex
);
782 if(getLast3DDecomposition().empty())
784 const Primitive3DContainer
aNewSequence(impCreate3DDecomposition(rViewInformation
));
785 const_cast< PolygonTubePrimitive3D
* >(this)->maLast3DDecomposition
= aNewSequence
;
788 return getLast3DDecomposition();
792 ImplPrimitive3DIDBlock(PolygonTubePrimitive3D
, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D
)
794 } // end of namespace primitive3d
795 } // end of namespace drawinglayer
797 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */