LibreOffice Module drawinglayer (master) 1
polygontubeprimitive3d.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
28#include <mutex>
29
31{
32 namespace // anonymous namespace
33 {
34 class TubeBuffer
35 {
36 private:
37 // data for buffered tube primitives
38 Primitive3DContainer m_aLineTubeList;
40 attribute::MaterialAttribute3D m_aLineMaterial;
41 std::mutex m_aMutex;
42 public:
43 TubeBuffer()
45 {
46 }
47
48 TubeBuffer(const TubeBuffer&) = delete;
49 const TubeBuffer& operator=(const TubeBuffer&) = delete;
50
51 Primitive3DContainer getLineTubeSegments(
52 sal_uInt32 nSegments,
53 const attribute::MaterialAttribute3D& rMaterial)
54 {
55 // may exclusively change cached data, use mutex
56 std::unique_lock aGuard(m_aMutex);
57
58 if (nSegments != m_nLineTubeSegments || !(rMaterial == m_aLineMaterial))
59 {
60 m_nLineTubeSegments = nSegments;
61 m_aLineMaterial = rMaterial;
62 m_aLineTubeList = Primitive3DContainer();
63 }
64
65 if (m_aLineTubeList.empty() && m_nLineTubeSegments != 0)
66 {
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);
72 aRot.rotate(2 * M_PI / static_cast<double>(m_nLineTubeSegments), 0.0, 0.0);
74
75 for(sal_uInt32 a = 0; a < m_nLineTubeSegments; ++a)
76 {
77 const basegfx::B3DPoint aNextLeft(aRot * aLastLeft);
78 const basegfx::B3DPoint aNextRight(aRot * aLastRight);
79 basegfx::B3DPolygon aNewPolygon;
80
81 aNewPolygon.append(aNextLeft);
82 aNewPolygon.setNormal(0, basegfx::B3DVector(aNextLeft - aLeft));
83
84 aNewPolygon.append(aLastLeft);
85 aNewPolygon.setNormal(1, basegfx::B3DVector(aLastLeft - aLeft));
86
87 aNewPolygon.append(aLastRight);
88 aNewPolygon.setNormal(2, basegfx::B3DVector(aLastRight - aRight));
89
90 aNewPolygon.append(aNextRight);
91 aNewPolygon.setNormal(3, basegfx::B3DVector(aNextRight - aRight));
92
93 aNewPolygon.setClosed(true);
94
95 basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
96 m_aLineTubeList[a] = new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), m_aLineMaterial, false);
97
98 aLastLeft = aNextLeft;
99 aLastRight = aNextRight;
100 }
101 }
102 return m_aLineTubeList;
103 }
104 };
105
106 Primitive3DContainer getLineTubeSegments(
107 sal_uInt32 nSegments,
108 const attribute::MaterialAttribute3D& rMaterial)
109 {
110 // static data for buffered tube primitives
111 static TubeBuffer theTubeBuffer;
112 return theTubeBuffer.getLineTubeSegments(nSegments, rMaterial);
113 }
114
115 class CapBuffer
116 {
117 private:
118 // data for buffered cap primitives
119 Primitive3DContainer m_aLineCapList;
121 attribute::MaterialAttribute3D m_aLineMaterial;
122 std::mutex m_aMutex;
123 public:
124 CapBuffer()
126 {
127 }
128 CapBuffer(const CapBuffer&) = delete;
129 const CapBuffer& operator=(const CapBuffer&) = delete;
130
131 Primitive3DContainer getLineCapSegments(
132 sal_uInt32 nSegments,
133 const attribute::MaterialAttribute3D& rMaterial)
134 {
135 // may exclusively change cached data, use mutex
136 std::unique_lock aGuard(m_aMutex);
137
138 if (nSegments != m_nLineCapSegments || !(rMaterial == m_aLineMaterial))
139 {
140 m_nLineCapSegments = nSegments;
141 m_aLineMaterial = rMaterial;
142 m_aLineCapList = Primitive3DContainer();
143 }
144
145 if (m_aLineCapList.empty() && m_nLineCapSegments != 0)
146 {
147 const basegfx::B3DPoint aNull(0.0, 0.0, 0.0);
148 basegfx::B3DPoint aLast(0.0, 1.0, 0.0);
150 aRot.rotate(2 * M_PI / static_cast<double>(m_nLineCapSegments), 0.0, 0.0);
152
153 for(sal_uInt32 a = 0; a < m_nLineCapSegments; ++a)
154 {
155 const basegfx::B3DPoint aNext(aRot * aLast);
156 basegfx::B3DPolygon aNewPolygon;
157
158 aNewPolygon.append(aLast);
159 aNewPolygon.setNormal(0, basegfx::B3DVector(aLast - aNull));
160
161 aNewPolygon.append(aNext);
162 aNewPolygon.setNormal(1, basegfx::B3DVector(aNext - aNull));
163
164 aNewPolygon.append(aNull);
165 aNewPolygon.setNormal(2, basegfx::B3DVector(-1.0, 0.0, 0.0));
166
167 aNewPolygon.setClosed(true);
168
169 basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
170 m_aLineCapList[a] = new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), m_aLineMaterial, false);
171
172 aLast = aNext;
173 }
174 }
175
176 return m_aLineCapList;
177 }
178 };
179
180 Primitive3DContainer getLineCapSegments(
181 sal_uInt32 nSegments,
182 const attribute::MaterialAttribute3D& rMaterial)
183 {
184 // static data for buffered cap primitives
185 static CapBuffer theCapBuffer;
186 return theCapBuffer.getLineCapSegments(nSegments, rMaterial);
187 }
188
189 class CapRoundBuffer
190 {
191 private:
192 // data for buffered capround primitives
193 Primitive3DContainer m_aLineCapRoundList;
195 attribute::MaterialAttribute3D m_aLineMaterial;
196 std::mutex m_aMutex;
197 public:
198 CapRoundBuffer()
200 {
201 }
202 CapRoundBuffer(const CapRoundBuffer&) = delete;
203 const CapRoundBuffer& operator=(const CapRoundBuffer&) = delete;
204
205 Primitive3DContainer getLineCapRoundSegments(
206 sal_uInt32 nSegments,
207 const attribute::MaterialAttribute3D& rMaterial)
208 {
209 // may exclusively change cached data, use mutex
210 std::unique_lock aGuard(m_aMutex);
211
212 if (nSegments != m_nLineCapRoundSegments || !(rMaterial == m_aLineMaterial))
213 {
214 m_nLineCapRoundSegments = nSegments;
215 m_aLineMaterial = rMaterial;
216 m_aLineCapRoundList = Primitive3DContainer();
217 }
218
220 {
221 // calculate new horizontal segments
222 sal_uInt32 nVerSeg(nSegments / 2);
223
224 if (nVerSeg < 1)
225 {
226 nVerSeg = 1;
227 }
228
229 // create half-sphere; upper half of unit sphere
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());
238
239 if (nCount)
240 {
241 // rotate to have sphere cap oriented to negative X-Axis; do not
242 // forget to transform normals, too
243 basegfx::B3DHomMatrix aSphereTrans;
244
245 aSphereTrans.rotate(0.0, 0.0, M_PI_2);
246 aSphere.transform(aSphereTrans);
247 aSphere.transformNormals(aSphereTrans);
248
249 // realloc for primitives and create based on polygon snippets
250 m_aLineCapRoundList.resize(nCount);
251
252 for (sal_uInt32 a = 0; a < nCount; ++a)
253 {
254 const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
255 basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
256
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);
263 }
264 }
265 }
266
267 return m_aLineCapRoundList;
268 }
269
270 };
271
272 Primitive3DContainer getLineCapRoundSegments(
273 sal_uInt32 nSegments,
274 const attribute::MaterialAttribute3D& rMaterial)
275 {
276 // static data for buffered cap primitives
277 static CapRoundBuffer theCapRoundBuffer;
278 return theCapRoundBuffer.getLineCapRoundSegments(nSegments, rMaterial);
279 }
280
281 Primitive3DContainer getLineJoinSegments(
282 sal_uInt32 nSegments,
283 const attribute::MaterialAttribute3D& rMaterial,
284 double fAngle,
285 double fMiterMinimumAngle,
286 basegfx::B2DLineJoin aLineJoin)
287 {
288 // nSegments is for whole circle, adapt to half circle
289 const sal_uInt32 nVerSeg(nSegments >> 1);
290 std::vector< BasePrimitive3D* > aResultVector;
291
292 if(nVerSeg)
293 {
294 if(basegfx::B2DLineJoin::Round == aLineJoin)
295 {
296 // calculate new horizontal segments
297 const sal_uInt32 nHorSeg(basegfx::fround((fAngle / (2 * M_PI)) * static_cast<double>(nSegments)));
298
299 if(nHorSeg)
300 {
301 // create half-sphere
302 const basegfx::B3DPolyPolygon aSphere(basegfx::utils::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, M_PI_2, -M_PI_2, 0.0, fAngle));
303
304 for(sal_uInt32 a(0); a < aSphere.count(); a++)
305 {
306 const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
307 basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
308 aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aPartPolyPolygon), rMaterial, false));
309 }
310 }
311 else
312 {
313 // fallback to bevel when there is not at least one segment hor and ver
314 aLineJoin = basegfx::B2DLineJoin::Bevel;
315 }
316 }
317
318 if (basegfx::B2DLineJoin::Bevel == aLineJoin ||
319 basegfx::B2DLineJoin::Miter == aLineJoin)
320 {
321 if(basegfx::B2DLineJoin::Miter == aLineJoin)
322 {
323 const double fMiterAngle(fAngle/2.0);
324
325 if(fMiterAngle < fMiterMinimumAngle)
326 {
327 // fallback to bevel when miter's angle is too small
328 aLineJoin = basegfx::B2DLineJoin::Bevel;
329 }
330 }
331
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;
342
343 // close polygon
344 aNewPolygon.setClosed(true);
345 aMiterPolygon.setClosed(true);
346
347 for(sal_uInt32 a(0); a < nVerSeg; a++)
348 {
349 const bool bFirst(0 == a);
350 const bool bLast(a + 1 == nVerSeg);
351
352 if(bFirst || !bLast)
353 {
354 fPos += fInc;
355
356 aNextPointOnXY = basegfx::B3DPoint(
357 cos(fPos),
358 sin(fPos),
359 0.0);
360
361 aNextPointRotY = basegfx::B3DPoint(
362 aNextPointOnXY.getX() * fCos,
363 aNextPointOnXY.getY(),
364 aNextPointOnXY.getX() * fSin);
365
366 if(bMiter)
367 {
368 aNextMiter = basegfx::B3DPoint(
369 aNextPointOnXY.getX(),
370 aNextPointOnXY.getY(),
371 fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
372 }
373 }
374
375 if(bFirst)
376 {
377 aNewPolygon.clear();
378
379 if(bMiter)
380 {
381 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
382 aNewPolygon.append(aNextPointOnXY);
383 aNewPolygon.append(aNextMiter);
384
385 aMiterPolygon.clear();
386 aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
387 aMiterPolygon.append(aNextMiter);
388 aMiterPolygon.append(aNextPointRotY);
389 }
390 else
391 {
392 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
393 aNewPolygon.append(aNextPointOnXY);
394 aNewPolygon.append(aNextPointRotY);
395 }
396 }
397 else if(bLast)
398 {
399 aNewPolygon.clear();
400
401 if(bMiter)
402 {
403 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
404 aNewPolygon.append(aCurrMiter);
405 aNewPolygon.append(aPointOnXY);
406
407 aMiterPolygon.clear();
408 aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
409 aMiterPolygon.append(aPointRotY);
410 aMiterPolygon.append(aCurrMiter);
411 }
412 else
413 {
414 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
415 aNewPolygon.append(aPointRotY);
416 aNewPolygon.append(aPointOnXY);
417 }
418 }
419 else
420 {
421 aNewPolygon.clear();
422
423 if(bMiter)
424 {
425 aNewPolygon.append(aPointOnXY);
426 aNewPolygon.append(aNextPointOnXY);
427 aNewPolygon.append(aNextMiter);
428 aNewPolygon.append(aCurrMiter);
429
430 aMiterPolygon.clear();
431 aMiterPolygon.append(aCurrMiter);
432 aMiterPolygon.append(aNextMiter);
433 aMiterPolygon.append(aNextPointRotY);
434 aMiterPolygon.append(aPointRotY);
435 }
436 else
437 {
438 aNewPolygon.append(aPointRotY);
439 aNewPolygon.append(aPointOnXY);
440 aNewPolygon.append(aNextPointOnXY);
441 aNewPolygon.append(aNextPointRotY);
442 }
443 }
444
445 // set normals
446 for(sal_uInt32 b(0); b < aNewPolygon.count(); b++)
447 {
448 aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
449 }
450
451 // create primitive
452 if(aNewPolygon.count())
453 {
454 basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
455 aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), rMaterial, false));
456 }
457
458 if(bMiter && aMiterPolygon.count())
459 {
460 // set normals
461 for(sal_uInt32 c(0); c < aMiterPolygon.count(); c++)
462 {
463 aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c)));
464 }
465
466 // create primitive
467 basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
468 aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aMiterPolyPolygon), rMaterial, false));
469 }
470
471 // prepare next step
472 if(bFirst || !bLast)
473 {
474 aPointOnXY = aNextPointOnXY;
475 aPointRotY = aNextPointRotY;
476
477 if(bMiter)
478 {
479 aCurrMiter = aNextMiter;
480 }
481 }
482 }
483 }
484 }
485
486 Primitive3DContainer aRetval(aResultVector.size());
487
488 std::transform(aResultVector.cbegin(), aResultVector.cend(), aRetval.begin(), [](auto &rResult){return Primitive3DReference(rResult);});
489
490 return aRetval;
491 }
492
493 basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
494 {
495 // build transformation from unit vector to vector
496 basegfx::B3DHomMatrix aRetval;
497
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()));
501
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);
505
506 return aRetval;
507 }
508 } // end of anonymous namespace
509
510
511using namespace com::sun::star;
512
514 {
515 const sal_uInt32 nPointCount(getB3DPolygon().count());
516 std::vector< BasePrimitive3D* > aResultVector;
517
518 if(nPointCount)
519 {
521 {
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));
529
530 for(sal_uInt32 a(0); a < nLoopCount; a++)
531 {
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());
536
537 if(basegfx::fTools::more(fForwLen, 0.0))
538 {
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());
545
546 // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
547 basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
548
549 // prepare transformations for tube and cap
550 basegfx::B3DHomMatrix aTubeTrans;
551 basegfx::B3DHomMatrix aCapTrans;
552
553 // cap gets radius size
554 aCapTrans.scale(getRadius(), getRadius(), getRadius());
555
556 if(bLineCapSquare)
557 {
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());
561
562 aTubeTrans.scale(fForwLen + fExtraLength, getRadius(), getRadius());
563
564 if(bFirst)
565 {
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);
569 }
570 }
571 else
572 {
573 // normal tube size
574 aTubeTrans.scale(fForwLen, getRadius(), getRadius());
575 }
576
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());
582
583 if(bNoLineJoin || (!bClosed && bFirst))
584 {
585 // line start edge, build transformed primitiveVector3D
586 Primitive3DContainer aSequence;
587
588 if(bLineCapRound && bFirst)
589 {
590 // LineCapRound used
591 aSequence = getLineCapRoundSegments(nSegments, aMaterial);
592 }
593 else
594 {
595 // simple closing cap
596 aSequence = getLineCapSegments(nSegments, aMaterial);
597 }
598
599 aResultVector.push_back(new TransformPrimitive3D(std::move(aCapTrans), aSequence));
600 }
601 else
602 {
603 const basegfx::B3DVector aBack(aCurr - aLast);
604 const double fCross(basegfx::cross(aBack, aForw).getLength());
605
606 if(!basegfx::fTools::equalZero(fCross))
607 {
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,
616 getLineJoin()));
617
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()));
623
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());
632
633 // line start edge, build transformed primitiveVector3D
634 aResultVector.push_back(
636 std::move(aSphereTrans),
637 aNewList));
638 }
639 }
640
641 // create line segments, build transformed primitiveVector3D
642 aResultVector.push_back(
644 std::move(aTubeTrans),
645 getLineTubeSegments(nSegments, aMaterial)));
646
647 if(bNoLineJoin || (!bClosed && bLast))
648 {
649 // line end edge
650 basegfx::B3DHomMatrix aBackCapTrans;
651
652 // Mirror (line end) and radius scale
653 aBackCapTrans.rotate(0.0, M_PI, 0.0);
654 aBackCapTrans.scale(getRadius(), getRadius(), getRadius());
655
656 if(bLineCapSquare && bLast)
657 {
658 // correct position when square and prolonged
659 aBackCapTrans.translate(fForwLen + getRadius(), 0.0, 0.0);
660 }
661 else
662 {
663 // standard position
664 aBackCapTrans.translate(fForwLen, 0.0, 0.0);
665 }
666
667 // rotate and translate to destination
668 aBackCapTrans *= aRotVector;
669 aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
670
671 // get primitiveVector3D
672 Primitive3DContainer aSequence;
673
674 if(bLineCapRound && bLast)
675 {
676 // LineCapRound used
677 aSequence = getLineCapRoundSegments(nSegments, aMaterial);
678 }
679 else
680 {
681 // simple closing cap
682 aSequence = getLineCapSegments(nSegments, aMaterial);
683 }
684
685 aResultVector.push_back(
687 std::move(aBackCapTrans),
688 aSequence));
689 }
690 }
691
692 // prepare next loop step
693 aLast = aCurr;
694 aCurr = aNext;
695 }
696 }
697 else
698 {
699 // create hairline
700 aResultVector.push_back(new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor()));
701 }
702 }
703
704 // prepare return value
705 Primitive3DContainer aRetval(aResultVector.size());
706
707 std::transform(aResultVector.cbegin(), aResultVector.cend(), aRetval.begin(), [](auto &rResult){return Primitive3DReference(rResult);});
708
709 return aRetval;
710 }
711
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)
725 {
726 }
727
729 {
730 if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
731 {
732 const PolygonTubePrimitive3D& rCompare = static_cast<const PolygonTubePrimitive3D&>(rPrimitive);
733
734 return (getRadius() == rCompare.getRadius()
735 && getDegreeStepWidth() == rCompare.getDegreeStepWidth()
737 && getLineJoin() == rCompare.getLineJoin()
738 && getLineCap() == rCompare.getLineCap());
739 }
740
741 return false;
742 }
743
745 {
746 std::unique_lock aGuard( m_aMutex );
747
748 if(getLast3DDecomposition().empty())
749 {
750 const Primitive3DContainer aNewSequence(impCreate3DDecomposition(rViewInformation));
751 const_cast< PolygonTubePrimitive3D* >(this)->maLast3DDecomposition = aNewSequence;
752 }
753
754 return getLast3DDecomposition();
755 }
756
757 // provide unique ID
759
760}
761/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void rotate(double fAngleX, double fAngleY, double fAngleZ)
void translate(double fX, double fY, double fZ)
void scale(double fX, double fY, double fZ)
void append(const B3DPoint &rPoint, sal_uInt32 nCount=1)
B3DPoint const & getB3DPoint(sal_uInt32 nIndex) const
sal_uInt32 count() const
void setNormal(sal_uInt32 nIndex, const B3DVector &rValue)
void setClosed(bool bNew)
double getLength() const
double getXZLength() const
double scalar(const B3DVector &rVec) const
TYPE getX() const
TYPE getZ() const
TYPE getY() const
PolygonHairlinePrimitive3D(basegfx::B3DPolygon aPolygon, const basegfx::BColor &rBColor)
constructor
const basegfx::B3DPolygon & getB3DPolygon() const
data read access
PolygonTubePrimitive3D(const basegfx::B3DPolygon &rPolygon, const basegfx::BColor &rBColor, double fRadius, basegfx::B2DLineJoin aLineJoin, css::drawing::LineCap aLineCap, double fDegreeStepWidth=basegfx::deg2rad(10.0), double fMiterMinimumAngle=basegfx::deg2rad(15.0))
constructor
Primitive3DContainer maLast3DDecomposition
hold the last decomposition since it's expensive
virtual Primitive3DContainer get3DDecomposition(const geometry::ViewInformation3D &rViewInformation) const override
local decomposition.
virtual bool operator==(const BasePrimitive3D &rPrimitive) const override
compare operator
const Primitive3DContainer & getLast3DDecomposition() const
access methods to maLast3DDecomposition.
Primitive3DContainer impCreate3DDecomposition(const geometry::ViewInformation3D &rViewInformation) const
local decomposition.
int nCount
#define PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D
uno_Any a
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
double getLength(const B2DPolygon &rCandidate)
B3DPolyPolygon createUnitSphereFillPolyPolygon(sal_uInt32 nHorSeg, sal_uInt32 nVerSeg, bool bNormals=false, double fVerStart=M_PI_2, double fVerStop=-M_PI_2, double fHorStart=0.0, double fHorStop=2 *M_PI)
B3DVector cross(const B3DVector &rVecA, const B3DVector &rVecB)
B2IRange fround(const B2DRange &rRange)
SdrPrimitive3D class.
ImplPrimitive3DIDBlock(PolygonHairlinePrimitive3D, PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D) Primitive3DContainer PolygonStrokePrimitive3D
attribute::MaterialAttribute3D m_aLineMaterial
Primitive3DContainer m_aLineCapList
std::mutex m_aMutex
sal_uInt32 m_nLineTubeSegments
sal_uInt32 m_nLineCapSegments
Primitive3DContainer m_aLineTubeList
Primitive3DContainer m_aLineCapRoundList
sal_uInt32 m_nLineCapRoundSegments