tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / drawinglayer / source / primitive3d / polygontubeprimitive3d.cxx
blob672c78463a7d6604a5238f8531e3a458e7e31e4d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <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 <mutex>
30 namespace drawinglayer::primitive3d
32 namespace // anonymous namespace
34 class TubeBuffer
36 private:
37 // data for buffered tube primitives
38 Primitive3DContainer m_aLineTubeList;
39 sal_uInt32 m_nLineTubeSegments;
40 attribute::MaterialAttribute3D m_aLineMaterial;
41 std::mutex m_aMutex;
42 public:
43 TubeBuffer()
44 : m_nLineTubeSegments(0)
48 TubeBuffer(const TubeBuffer&) = delete;
49 const TubeBuffer& operator=(const TubeBuffer&) = delete;
51 Primitive3DContainer getLineTubeSegments(
52 sal_uInt32 nSegments,
53 const attribute::MaterialAttribute3D& rMaterial)
55 // may exclusively change cached data, use mutex
56 std::unique_lock aGuard(m_aMutex);
58 if (nSegments != m_nLineTubeSegments || !(rMaterial == m_aLineMaterial))
60 m_nLineTubeSegments = nSegments;
61 m_aLineMaterial = rMaterial;
62 m_aLineTubeList = Primitive3DContainer();
65 if (m_aLineTubeList.empty() && m_nLineTubeSegments != 0)
67 const basegfx::B3DPoint aLeft(0.0, 0.0, 0.0);
68 const basegfx::B3DPoint aRight(1.0, 0.0, 0.0);
69 basegfx::B3DPoint aLastLeft(0.0, 1.0, 0.0);
70 basegfx::B3DPoint aLastRight(1.0, 1.0, 0.0);
71 basegfx::B3DHomMatrix aRot;
72 aRot.rotate(2 * M_PI / static_cast<double>(m_nLineTubeSegments), 0.0, 0.0);
73 m_aLineTubeList.resize(m_nLineTubeSegments);
75 for(sal_uInt32 a = 0; a < m_nLineTubeSegments; ++a)
77 const basegfx::B3DPoint aNextLeft(aRot * aLastLeft);
78 const basegfx::B3DPoint aNextRight(aRot * aLastRight);
79 basegfx::B3DPolygon aNewPolygon;
81 aNewPolygon.append(aNextLeft);
82 aNewPolygon.setNormal(0, basegfx::B3DVector(aNextLeft - aLeft));
84 aNewPolygon.append(aLastLeft);
85 aNewPolygon.setNormal(1, basegfx::B3DVector(aLastLeft - aLeft));
87 aNewPolygon.append(aLastRight);
88 aNewPolygon.setNormal(2, basegfx::B3DVector(aLastRight - aRight));
90 aNewPolygon.append(aNextRight);
91 aNewPolygon.setNormal(3, basegfx::B3DVector(aNextRight - aRight));
93 aNewPolygon.setClosed(true);
95 basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
96 m_aLineTubeList[a] = new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), m_aLineMaterial, false);
98 aLastLeft = aNextLeft;
99 aLastRight = aNextRight;
102 return m_aLineTubeList;
106 Primitive3DContainer getLineTubeSegments(
107 sal_uInt32 nSegments,
108 const attribute::MaterialAttribute3D& rMaterial)
110 // static data for buffered tube primitives
111 static TubeBuffer theTubeBuffer;
112 return theTubeBuffer.getLineTubeSegments(nSegments, rMaterial);
115 class CapBuffer
117 private:
118 // data for buffered cap primitives
119 Primitive3DContainer m_aLineCapList;
120 sal_uInt32 m_nLineCapSegments;
121 attribute::MaterialAttribute3D m_aLineMaterial;
122 std::mutex m_aMutex;
123 public:
124 CapBuffer()
125 : m_nLineCapSegments(0)
128 CapBuffer(const CapBuffer&) = delete;
129 const CapBuffer& operator=(const CapBuffer&) = delete;
131 Primitive3DContainer getLineCapSegments(
132 sal_uInt32 nSegments,
133 const attribute::MaterialAttribute3D& rMaterial)
135 // may exclusively change cached data, use mutex
136 std::unique_lock aGuard(m_aMutex);
138 if (nSegments != m_nLineCapSegments || !(rMaterial == m_aLineMaterial))
140 m_nLineCapSegments = nSegments;
141 m_aLineMaterial = rMaterial;
142 m_aLineCapList = Primitive3DContainer();
145 if (m_aLineCapList.empty() && m_nLineCapSegments != 0)
147 const basegfx::B3DPoint aNull(0.0, 0.0, 0.0);
148 basegfx::B3DPoint aLast(0.0, 1.0, 0.0);
149 basegfx::B3DHomMatrix aRot;
150 aRot.rotate(2 * M_PI / static_cast<double>(m_nLineCapSegments), 0.0, 0.0);
151 m_aLineCapList.resize(m_nLineCapSegments);
153 for(sal_uInt32 a = 0; a < m_nLineCapSegments; ++a)
155 const basegfx::B3DPoint aNext(aRot * aLast);
156 basegfx::B3DPolygon aNewPolygon;
158 aNewPolygon.append(aLast);
159 aNewPolygon.setNormal(0, basegfx::B3DVector(aLast - aNull));
161 aNewPolygon.append(aNext);
162 aNewPolygon.setNormal(1, basegfx::B3DVector(aNext - aNull));
164 aNewPolygon.append(aNull);
165 aNewPolygon.setNormal(2, basegfx::B3DVector(-1.0, 0.0, 0.0));
167 aNewPolygon.setClosed(true);
169 basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
170 m_aLineCapList[a] = new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), m_aLineMaterial, false);
172 aLast = aNext;
176 return m_aLineCapList;
180 Primitive3DContainer getLineCapSegments(
181 sal_uInt32 nSegments,
182 const attribute::MaterialAttribute3D& rMaterial)
184 // static data for buffered cap primitives
185 static CapBuffer theCapBuffer;
186 return theCapBuffer.getLineCapSegments(nSegments, rMaterial);
189 class CapRoundBuffer
191 private:
192 // data for buffered capround primitives
193 Primitive3DContainer m_aLineCapRoundList;
194 sal_uInt32 m_nLineCapRoundSegments;
195 attribute::MaterialAttribute3D m_aLineMaterial;
196 std::mutex m_aMutex;
197 public:
198 CapRoundBuffer()
199 : m_nLineCapRoundSegments(0)
202 CapRoundBuffer(const CapRoundBuffer&) = delete;
203 const CapRoundBuffer& operator=(const CapRoundBuffer&) = delete;
205 Primitive3DContainer getLineCapRoundSegments(
206 sal_uInt32 nSegments,
207 const attribute::MaterialAttribute3D& rMaterial)
209 // may exclusively change cached data, use mutex
210 std::unique_lock aGuard(m_aMutex);
212 if (nSegments != m_nLineCapRoundSegments || !(rMaterial == m_aLineMaterial))
214 m_nLineCapRoundSegments = nSegments;
215 m_aLineMaterial = rMaterial;
216 m_aLineCapRoundList = Primitive3DContainer();
219 if (m_aLineCapRoundList.empty() && m_nLineCapRoundSegments)
221 // calculate new horizontal segments
222 sal_uInt32 nVerSeg(nSegments / 2);
224 if (nVerSeg < 1)
226 nVerSeg = 1;
229 // create half-sphere; upper half of unit sphere
230 basegfx::B3DPolyPolygon aSphere(
231 basegfx::utils::createUnitSphereFillPolyPolygon(
232 nSegments,
233 nVerSeg,
234 true,
235 M_PI_2, 0.0,
236 0.0, 2 * M_PI));
237 const sal_uInt32 nCount(aSphere.count());
239 if (nCount)
241 // rotate to have sphere cap oriented to negative X-Axis; do not
242 // forget to transform normals, too
243 basegfx::B3DHomMatrix aSphereTrans;
245 aSphereTrans.rotate(0.0, 0.0, M_PI_2);
246 aSphere.transform(aSphereTrans);
247 aSphere.transformNormals(aSphereTrans);
249 // realloc for primitives and create based on polygon snippets
250 m_aLineCapRoundList.resize(nCount);
252 for (sal_uInt32 a = 0; a < nCount; ++a)
254 const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
255 basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
257 // need to create one primitive per Polygon since the primitive
258 // is for planar PolyPolygons which is definitely not the case here
259 m_aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D(
260 std::move(aPartPolyPolygon),
261 rMaterial,
262 false);
267 return m_aLineCapRoundList;
272 Primitive3DContainer getLineCapRoundSegments(
273 sal_uInt32 nSegments,
274 const attribute::MaterialAttribute3D& rMaterial)
276 // static data for buffered cap primitives
277 static CapRoundBuffer theCapRoundBuffer;
278 return theCapRoundBuffer.getLineCapRoundSegments(nSegments, rMaterial);
281 Primitive3DContainer getLineJoinSegments(
282 sal_uInt32 nSegments,
283 const attribute::MaterialAttribute3D& rMaterial,
284 double fAngle,
285 double fMiterMinimumAngle,
286 basegfx::B2DLineJoin aLineJoin)
288 // nSegments is for whole circle, adapt to half circle
289 const sal_uInt32 nVerSeg(nSegments >> 1);
290 std::vector< BasePrimitive3D* > aResultVector;
292 if(nVerSeg)
294 if(basegfx::B2DLineJoin::Round == aLineJoin)
296 // calculate new horizontal segments
297 const sal_uInt32 nHorSeg(basegfx::fround((fAngle / (2 * M_PI)) * static_cast<double>(nSegments)));
299 if(nHorSeg)
301 // create half-sphere
302 const basegfx::B3DPolyPolygon aSphere(basegfx::utils::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, M_PI_2, -M_PI_2, 0.0, fAngle));
304 for(sal_uInt32 a(0); a < aSphere.count(); a++)
306 const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
307 basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
308 aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aPartPolyPolygon), rMaterial, false));
311 else
313 // fallback to bevel when there is not at least one segment hor and ver
314 aLineJoin = basegfx::B2DLineJoin::Bevel;
318 if (basegfx::B2DLineJoin::Bevel == aLineJoin ||
319 basegfx::B2DLineJoin::Miter == aLineJoin)
321 if(basegfx::B2DLineJoin::Miter == aLineJoin)
323 const double fMiterAngle(fAngle/2.0);
325 if(fMiterAngle < fMiterMinimumAngle)
327 // fallback to bevel when miter's angle is too small
328 aLineJoin = basegfx::B2DLineJoin::Bevel;
332 const double fInc(M_PI / static_cast<double>(nVerSeg));
333 const double fSin(sin(-fAngle));
334 const double fCos(cos(-fAngle));
335 const bool bMiter(basegfx::B2DLineJoin::Miter == aLineJoin);
336 const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0);
337 const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0);
338 double fPos(-M_PI_2);
339 basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY;
340 basegfx::B3DPoint aCurrMiter, aNextMiter;
341 basegfx::B3DPolygon aNewPolygon, aMiterPolygon;
343 // close polygon
344 aNewPolygon.setClosed(true);
345 aMiterPolygon.setClosed(true);
347 for(sal_uInt32 a(0); a < nVerSeg; a++)
349 const bool bFirst(0 == a);
350 const bool bLast(a + 1 == nVerSeg);
352 if(bFirst || !bLast)
354 fPos += fInc;
356 aNextPointOnXY = basegfx::B3DPoint(
357 cos(fPos),
358 sin(fPos),
359 0.0);
361 aNextPointRotY = basegfx::B3DPoint(
362 aNextPointOnXY.getX() * fCos,
363 aNextPointOnXY.getY(),
364 aNextPointOnXY.getX() * fSin);
366 if(bMiter)
368 aNextMiter = basegfx::B3DPoint(
369 aNextPointOnXY.getX(),
370 aNextPointOnXY.getY(),
371 fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
375 if(bFirst)
377 aNewPolygon.clear();
379 if(bMiter)
381 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
382 aNewPolygon.append(aNextPointOnXY);
383 aNewPolygon.append(aNextMiter);
385 aMiterPolygon.clear();
386 aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
387 aMiterPolygon.append(aNextMiter);
388 aMiterPolygon.append(aNextPointRotY);
390 else
392 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
393 aNewPolygon.append(aNextPointOnXY);
394 aNewPolygon.append(aNextPointRotY);
397 else if(bLast)
399 aNewPolygon.clear();
401 if(bMiter)
403 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
404 aNewPolygon.append(aCurrMiter);
405 aNewPolygon.append(aPointOnXY);
407 aMiterPolygon.clear();
408 aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
409 aMiterPolygon.append(aPointRotY);
410 aMiterPolygon.append(aCurrMiter);
412 else
414 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
415 aNewPolygon.append(aPointRotY);
416 aNewPolygon.append(aPointOnXY);
419 else
421 aNewPolygon.clear();
423 if(bMiter)
425 aNewPolygon.append(aPointOnXY);
426 aNewPolygon.append(aNextPointOnXY);
427 aNewPolygon.append(aNextMiter);
428 aNewPolygon.append(aCurrMiter);
430 aMiterPolygon.clear();
431 aMiterPolygon.append(aCurrMiter);
432 aMiterPolygon.append(aNextMiter);
433 aMiterPolygon.append(aNextPointRotY);
434 aMiterPolygon.append(aPointRotY);
436 else
438 aNewPolygon.append(aPointRotY);
439 aNewPolygon.append(aPointOnXY);
440 aNewPolygon.append(aNextPointOnXY);
441 aNewPolygon.append(aNextPointRotY);
445 // set normals
446 for(sal_uInt32 b(0); b < aNewPolygon.count(); b++)
448 aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
451 // create primitive
452 if(aNewPolygon.count())
454 basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
455 aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), rMaterial, false));
458 if(bMiter && aMiterPolygon.count())
460 // set normals
461 for(sal_uInt32 c(0); c < aMiterPolygon.count(); c++)
463 aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c)));
466 // create primitive
467 basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
468 aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aMiterPolyPolygon), rMaterial, false));
471 // prepare next step
472 if(bFirst || !bLast)
474 aPointOnXY = aNextPointOnXY;
475 aPointRotY = aNextPointRotY;
477 if(bMiter)
479 aCurrMiter = aNextMiter;
486 Primitive3DContainer aRetval(aResultVector.size());
488 std::transform(aResultVector.cbegin(), aResultVector.cend(), aRetval.begin(), [](auto &rResult){return Primitive3DReference(rResult);});
490 return aRetval;
493 basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
495 // build transformation from unit vector to vector
496 basegfx::B3DHomMatrix aRetval;
498 // get applied rotations from angles in XY and in XZ (cartesian)
499 const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength()));
500 const double fRotInXZ(atan2(-rVector.getZ(), rVector.getX()));
502 // apply rotations. Rot around Z needs to be done first, so apply in two steps
503 aRetval.rotate(0.0, 0.0, fRotInXY);
504 aRetval.rotate(0.0, fRotInXZ, 0.0);
506 return aRetval;
508 } // end of anonymous namespace
511 using namespace com::sun::star;
513 Primitive3DContainer PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
515 const sal_uInt32 nPointCount(getB3DPolygon().count());
516 std::vector< BasePrimitive3D* > aResultVector;
518 if(nPointCount)
520 if(getRadius() > 0.0)
522 const attribute::MaterialAttribute3D aMaterial(getBColor());
523 static const sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps)
524 const bool bClosed(getB3DPolygon().isClosed());
525 const bool bNoLineJoin(basegfx::B2DLineJoin::NONE == getLineJoin());
526 const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1);
527 basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1));
528 basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0));
530 for(sal_uInt32 a(0); a < nLoopCount; a++)
532 // get next data
533 const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1) % nPointCount));
534 const basegfx::B3DVector aForw(aNext - aCurr);
535 const double fForwLen(aForw.getLength());
537 if(fForwLen > 0.0)
539 // find out if linecap is active
540 const bool bFirst(!a);
541 const bool bLast(a + 1 == nLoopCount);
542 const bool bLineCapPossible(!bClosed && (bFirst || bLast));
543 const bool bLineCapRound(bLineCapPossible && css::drawing::LineCap_ROUND == getLineCap());
544 const bool bLineCapSquare(bLineCapPossible && css::drawing::LineCap_SQUARE == getLineCap());
546 // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
547 basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
549 // prepare transformations for tube and cap
550 basegfx::B3DHomMatrix aTubeTrans;
551 basegfx::B3DHomMatrix aCapTrans;
553 // cap gets radius size
554 aCapTrans.scale(getRadius(), getRadius(), getRadius());
556 if(bLineCapSquare)
558 // when square line cap just prolong line segment in X, maybe 2 x radius when
559 // first and last (simple line segment)
560 const double fExtraLength(bFirst && bLast ? getRadius() * 2.0 : getRadius());
562 aTubeTrans.scale(fForwLen + fExtraLength, getRadius(), getRadius());
564 if(bFirst)
566 // correct start positions for tube and cap when first and square prolonged
567 aTubeTrans.translate(-getRadius(), 0.0, 0.0);
568 aCapTrans.translate(-getRadius(), 0.0, 0.0);
571 else
573 // normal tube size
574 aTubeTrans.scale(fForwLen, getRadius(), getRadius());
577 // rotate and translate tube and cap
578 aTubeTrans *= aRotVector;
579 aTubeTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
580 aCapTrans *= aRotVector;
581 aCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
583 if(bNoLineJoin || (!bClosed && bFirst))
585 // line start edge, build transformed primitiveVector3D
586 Primitive3DContainer aSequence;
588 if(bLineCapRound && bFirst)
590 // LineCapRound used
591 aSequence = getLineCapRoundSegments(nSegments, aMaterial);
593 else
595 // simple closing cap
596 aSequence = getLineCapSegments(nSegments, aMaterial);
599 aResultVector.push_back(new TransformPrimitive3D(std::move(aCapTrans), aSequence));
601 else
603 const basegfx::B3DVector aBack(aCurr - aLast);
604 const double fCross(basegfx::cross(aBack, aForw).getLength());
606 if(!basegfx::fTools::equalZero(fCross))
608 // line connect non-parallel, aBack, aForw, use getLineJoin()
609 const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. M_PI_2
610 Primitive3DContainer aNewList(
611 getLineJoinSegments(
612 nSegments,
613 aMaterial,
614 fAngle,
615 getMiterMinimumAngle(),
616 getLineJoin()));
618 // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
619 basegfx::B3DHomMatrix aInvRotVector(aRotVector);
620 aInvRotVector.invert();
621 basegfx::B3DVector aTransBack(aInvRotVector * aBack);
622 const double fRotInYZ(atan2(aTransBack.getY(), aTransBack.getZ()));
624 // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
625 // Also apply usual scaling and translation
626 basegfx::B3DHomMatrix aSphereTrans;
627 aSphereTrans.rotate(0.0, M_PI_2, 0.0);
628 aSphereTrans.rotate(M_PI - fRotInYZ, 0.0, 0.0);
629 aSphereTrans *= aRotVector;
630 aSphereTrans.scale(getRadius(), getRadius(), getRadius());
631 aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
633 // line start edge, build transformed primitiveVector3D
634 aResultVector.push_back(
635 new TransformPrimitive3D(
636 std::move(aSphereTrans),
637 aNewList));
641 // create line segments, build transformed primitiveVector3D
642 aResultVector.push_back(
643 new TransformPrimitive3D(
644 std::move(aTubeTrans),
645 getLineTubeSegments(nSegments, aMaterial)));
647 if(bNoLineJoin || (!bClosed && bLast))
649 // line end edge
650 basegfx::B3DHomMatrix aBackCapTrans;
652 // Mirror (line end) and radius scale
653 aBackCapTrans.rotate(0.0, M_PI, 0.0);
654 aBackCapTrans.scale(getRadius(), getRadius(), getRadius());
656 if(bLineCapSquare && bLast)
658 // correct position when square and prolonged
659 aBackCapTrans.translate(fForwLen + getRadius(), 0.0, 0.0);
661 else
663 // standard position
664 aBackCapTrans.translate(fForwLen, 0.0, 0.0);
667 // rotate and translate to destination
668 aBackCapTrans *= aRotVector;
669 aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
671 // get primitiveVector3D
672 Primitive3DContainer aSequence;
674 if(bLineCapRound && bLast)
676 // LineCapRound used
677 aSequence = getLineCapRoundSegments(nSegments, aMaterial);
679 else
681 // simple closing cap
682 aSequence = getLineCapSegments(nSegments, aMaterial);
685 aResultVector.push_back(
686 new TransformPrimitive3D(
687 std::move(aBackCapTrans),
688 aSequence));
692 // prepare next loop step
693 aLast = aCurr;
694 aCurr = aNext;
697 else
699 // create hairline
700 aResultVector.push_back(new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor()));
704 // prepare return value
705 Primitive3DContainer aRetval(aResultVector.size());
707 std::transform(aResultVector.cbegin(), aResultVector.cend(), aRetval.begin(), [](auto &rResult){return Primitive3DReference(rResult);});
709 return aRetval;
712 PolygonTubePrimitive3D::PolygonTubePrimitive3D(
713 const basegfx::B3DPolygon& rPolygon,
714 const basegfx::BColor& rBColor,
715 double fRadius, basegfx::B2DLineJoin aLineJoin,
716 css::drawing::LineCap aLineCap,
717 double fDegreeStepWidth,
718 double fMiterMinimumAngle)
719 : PolygonHairlinePrimitive3D(rPolygon, rBColor),
720 mfRadius(fRadius),
721 mfDegreeStepWidth(fDegreeStepWidth),
722 mfMiterMinimumAngle(fMiterMinimumAngle),
723 maLineJoin(aLineJoin),
724 maLineCap(aLineCap)
728 bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
730 if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
732 const PolygonTubePrimitive3D& rCompare = static_cast<const PolygonTubePrimitive3D&>(rPrimitive);
734 return (getRadius() == rCompare.getRadius()
735 && getDegreeStepWidth() == rCompare.getDegreeStepWidth()
736 && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle()
737 && getLineJoin() == rCompare.getLineJoin()
738 && getLineCap() == rCompare.getLineCap());
741 return false;
744 Primitive3DContainer PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
746 std::unique_lock aGuard( m_aMutex );
748 if(getLast3DDecomposition().empty())
750 const Primitive3DContainer aNewSequence(impCreate3DDecomposition(rViewInformation));
751 const_cast< PolygonTubePrimitive3D* >(this)->maLast3DDecomposition = aNewSequence;
754 return getLast3DDecomposition();
757 // provide unique ID
758 ImplPrimitive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D)
761 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */