Line data Source code
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 :
20 : #include <boost/noncopyable.hpp>
21 : #include <drawinglayer/primitive3d/polygontubeprimitive3d.hxx>
22 : #include <drawinglayer/attribute/materialattribute3d.hxx>
23 : #include <basegfx/matrix/b3dhommatrix.hxx>
24 : #include <basegfx/polygon/b3dpolypolygon.hxx>
25 : #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
26 : #include <basegfx/polygon/b3dpolypolygontools.hxx>
27 : #include <drawinglayer/primitive3d/transformprimitive3d.hxx>
28 : #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
29 : #include <rtl/instance.hxx>
30 :
31 :
32 :
33 : namespace drawinglayer
34 : {
35 : namespace primitive3d
36 : {
37 : namespace // anonymous namespace
38 : {
39 4 : class TubeBuffer : boost::noncopyable
40 : {
41 : private:
42 : // data for buffered tube primitives
43 : Primitive3DSequence m_aLineTubeList;
44 : sal_uInt32 m_nLineTubeSegments;
45 : attribute::MaterialAttribute3D m_aLineMaterial;
46 : ::osl::Mutex m_aMutex;
47 : public:
48 4 : TubeBuffer()
49 4 : : m_nLineTubeSegments(0L)
50 : {
51 4 : }
52 :
53 9902 : Primitive3DSequence getLineTubeSegments(
54 : sal_uInt32 nSegments,
55 : const attribute::MaterialAttribute3D& rMaterial)
56 : {
57 : // may exclusively change cached data, use mutex
58 9902 : ::osl::MutexGuard aGuard(m_aMutex);
59 :
60 9902 : if (nSegments != m_nLineTubeSegments || !(rMaterial == m_aLineMaterial))
61 : {
62 20 : m_nLineTubeSegments = nSegments;
63 20 : m_aLineMaterial = rMaterial;
64 20 : m_aLineTubeList = Primitive3DSequence();
65 : }
66 :
67 9902 : if (!m_aLineTubeList.hasElements() && m_nLineTubeSegments != 0)
68 : {
69 20 : const basegfx::B3DPoint aLeft(0.0, 0.0, 0.0);
70 40 : const basegfx::B3DPoint aRight(1.0, 0.0, 0.0);
71 40 : basegfx::B3DPoint aLastLeft(0.0, 1.0, 0.0);
72 40 : basegfx::B3DPoint aLastRight(1.0, 1.0, 0.0);
73 40 : basegfx::B3DHomMatrix aRot;
74 20 : aRot.rotate(F_2PI / (double)m_nLineTubeSegments, 0.0, 0.0);
75 20 : m_aLineTubeList.realloc(m_nLineTubeSegments);
76 :
77 180 : for(sal_uInt32 a = 0; a < m_nLineTubeSegments; ++a)
78 : {
79 160 : const basegfx::B3DPoint aNextLeft(aRot * aLastLeft);
80 320 : const basegfx::B3DPoint aNextRight(aRot * aLastRight);
81 320 : basegfx::B3DPolygon aNewPolygon;
82 :
83 160 : aNewPolygon.append(aNextLeft);
84 160 : aNewPolygon.setNormal(0L, basegfx::B3DVector(aNextLeft - aLeft));
85 :
86 160 : aNewPolygon.append(aLastLeft);
87 160 : aNewPolygon.setNormal(1L, basegfx::B3DVector(aLastLeft - aLeft));
88 :
89 160 : aNewPolygon.append(aLastRight);
90 160 : aNewPolygon.setNormal(2L, basegfx::B3DVector(aLastRight - aRight));
91 :
92 160 : aNewPolygon.append(aNextRight);
93 160 : aNewPolygon.setNormal(3L, basegfx::B3DVector(aNextRight - aRight));
94 :
95 160 : aNewPolygon.setClosed(true);
96 :
97 320 : const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
98 320 : const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
99 160 : m_aLineTubeList[a] = xRef;
100 :
101 160 : aLastLeft = aNextLeft;
102 160 : aLastRight = aNextRight;
103 180 : }
104 : }
105 9902 : return m_aLineTubeList;
106 : }
107 : };
108 :
109 : struct theTubeBuffer :
110 : public rtl::Static< TubeBuffer, theTubeBuffer > {};
111 :
112 9902 : Primitive3DSequence getLineTubeSegments(
113 : sal_uInt32 nSegments,
114 : const attribute::MaterialAttribute3D& rMaterial)
115 : {
116 : // static data for buffered tube primitives
117 9902 : TubeBuffer &rTheBuffer = theTubeBuffer::get();
118 9902 : return rTheBuffer.getLineTubeSegments(nSegments, rMaterial);
119 : }
120 :
121 4 : class CapBuffer : boost::noncopyable
122 : {
123 : private:
124 : // data for buffered cap primitives
125 : Primitive3DSequence m_aLineCapList;
126 : sal_uInt32 m_nLineCapSegments;
127 : attribute::MaterialAttribute3D m_aLineMaterial;
128 : ::osl::Mutex m_aMutex;
129 : public:
130 4 : CapBuffer()
131 4 : : m_nLineCapSegments(0)
132 : {
133 4 : }
134 836 : Primitive3DSequence getLineCapSegments(
135 : sal_uInt32 nSegments,
136 : const attribute::MaterialAttribute3D& rMaterial)
137 : {
138 : // may exclusively change cached data, use mutex
139 836 : ::osl::MutexGuard aGuard(m_aMutex);
140 :
141 836 : if (nSegments != m_nLineCapSegments || !(rMaterial == m_aLineMaterial))
142 : {
143 20 : m_nLineCapSegments = nSegments;
144 20 : m_aLineMaterial = rMaterial;
145 20 : m_aLineCapList = Primitive3DSequence();
146 : }
147 :
148 836 : if (!m_aLineCapList.hasElements() && m_nLineCapSegments != 0)
149 : {
150 20 : const basegfx::B3DPoint aNull(0.0, 0.0, 0.0);
151 40 : basegfx::B3DPoint aLast(0.0, 1.0, 0.0);
152 40 : basegfx::B3DHomMatrix aRot;
153 20 : aRot.rotate(F_2PI / (double)m_nLineCapSegments, 0.0, 0.0);
154 20 : m_aLineCapList.realloc(m_nLineCapSegments);
155 :
156 180 : for(sal_uInt32 a = 0; a < m_nLineCapSegments; ++a)
157 : {
158 160 : const basegfx::B3DPoint aNext(aRot * aLast);
159 320 : basegfx::B3DPolygon aNewPolygon;
160 :
161 160 : aNewPolygon.append(aLast);
162 160 : aNewPolygon.setNormal(0L, basegfx::B3DVector(aLast - aNull));
163 :
164 160 : aNewPolygon.append(aNext);
165 160 : aNewPolygon.setNormal(1L, basegfx::B3DVector(aNext - aNull));
166 :
167 160 : aNewPolygon.append(aNull);
168 160 : aNewPolygon.setNormal(2L, basegfx::B3DVector(-1.0, 0.0, 0.0));
169 :
170 160 : aNewPolygon.setClosed(true);
171 :
172 320 : const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
173 320 : const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
174 160 : m_aLineCapList[a] = xRef;
175 :
176 160 : aLast = aNext;
177 180 : }
178 : }
179 :
180 836 : return m_aLineCapList;
181 : }
182 : };
183 :
184 : struct theCapBuffer :
185 : public rtl::Static< CapBuffer, theCapBuffer > {};
186 :
187 836 : Primitive3DSequence getLineCapSegments(
188 : sal_uInt32 nSegments,
189 : const attribute::MaterialAttribute3D& rMaterial)
190 : {
191 : // static data for buffered cap primitives
192 836 : CapBuffer &rTheBuffer = theCapBuffer::get();
193 836 : return rTheBuffer.getLineCapSegments(nSegments, rMaterial);
194 : }
195 :
196 0 : class CapRoundBuffer : boost::noncopyable
197 : {
198 : private:
199 : // data for buffered capround primitives
200 : Primitive3DSequence m_aLineCapRoundList;
201 : sal_uInt32 m_nLineCapRoundSegments;
202 : attribute::MaterialAttribute3D m_aLineMaterial;
203 : ::osl::Mutex m_aMutex;
204 : public:
205 0 : CapRoundBuffer()
206 0 : : m_nLineCapRoundSegments(0)
207 : {
208 0 : }
209 0 : Primitive3DSequence getLineCapRoundSegments(
210 : sal_uInt32 nSegments,
211 : const attribute::MaterialAttribute3D& rMaterial)
212 : {
213 : // may exclusively change cached data, use mutex
214 0 : ::osl::MutexGuard aGuard(m_aMutex);
215 :
216 0 : if (nSegments != m_nLineCapRoundSegments || !(rMaterial == m_aLineMaterial))
217 : {
218 0 : m_nLineCapRoundSegments = nSegments;
219 0 : m_aLineMaterial = rMaterial;
220 0 : m_aLineCapRoundList = Primitive3DSequence();
221 : }
222 :
223 0 : if (!m_aLineCapRoundList.hasElements() && m_nLineCapRoundSegments)
224 : {
225 : // calculate new horizontal segments
226 0 : sal_uInt32 nVerSeg(nSegments / 2);
227 :
228 0 : if (nVerSeg < 1)
229 : {
230 0 : nVerSeg = 1;
231 : }
232 :
233 : // create half-sphere; upper half of unit sphere
234 : basegfx::B3DPolyPolygon aSphere(
235 : basegfx::tools::createUnitSphereFillPolyPolygon(
236 : nSegments,
237 : nVerSeg,
238 : true,
239 : F_PI2, 0.0,
240 0 : 0.0, F_2PI));
241 0 : const sal_uInt32 nCount(aSphere.count());
242 :
243 0 : if (nCount)
244 : {
245 : // rotate to have sphere cap orientned to negative X-Axis; do not
246 : // forget to transform normals, too
247 0 : basegfx::B3DHomMatrix aSphereTrans;
248 :
249 0 : aSphereTrans.rotate(0.0, 0.0, F_PI2);
250 0 : aSphere.transform(aSphereTrans);
251 0 : aSphere.transformNormals(aSphereTrans);
252 :
253 : // realloc for primitives and create based on polygon snippets
254 0 : m_aLineCapRoundList.realloc(nCount);
255 :
256 0 : for (sal_uInt32 a = 0; a < nCount; ++a)
257 : {
258 0 : const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a));
259 0 : const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
260 :
261 : // need to create one primitive per Polygon since the primitive
262 : // is for planar PolyPolygons which is definitely not the case here
263 0 : m_aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D(
264 : aPartPolyPolygon,
265 : rMaterial,
266 0 : false);
267 0 : }
268 0 : }
269 : }
270 :
271 0 : return m_aLineCapRoundList;
272 : }
273 :
274 : };
275 :
276 : struct theCapRoundBuffer :
277 : public rtl::Static< CapRoundBuffer, theCapRoundBuffer > {};
278 :
279 :
280 0 : Primitive3DSequence getLineCapRoundSegments(
281 : sal_uInt32 nSegments,
282 : const attribute::MaterialAttribute3D& rMaterial)
283 : {
284 : // static data for buffered cap primitives
285 0 : CapRoundBuffer &rTheBuffer = theCapRoundBuffer::get();
286 0 : return rTheBuffer.getLineCapRoundSegments(nSegments, rMaterial);
287 : }
288 :
289 9484 : Primitive3DSequence getLineJoinSegments(
290 : sal_uInt32 nSegments,
291 : const attribute::MaterialAttribute3D& rMaterial,
292 : double fAngle,
293 : double /*fDegreeStepWidth*/,
294 : double fMiterMinimumAngle,
295 : basegfx::B2DLineJoin aLineJoin)
296 : {
297 : // nSegments is for whole circle, adapt to half circle
298 9484 : const sal_uInt32 nVerSeg(nSegments >> 1L);
299 9484 : std::vector< BasePrimitive3D* > aResultVector;
300 :
301 9484 : if(nVerSeg)
302 : {
303 9484 : if(basegfx::B2DLINEJOIN_ROUND == aLineJoin)
304 : {
305 : // calculate new horizontal segments
306 9484 : const sal_uInt32 nHorSeg(basegfx::fround((fAngle / F_2PI) * (double)nSegments));
307 :
308 9484 : if(nHorSeg)
309 : {
310 : // create half-sphere
311 268 : const basegfx::B3DPolyPolygon aSphere(basegfx::tools::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle));
312 :
313 2412 : for(sal_uInt32 a(0L); a < aSphere.count(); a++)
314 : {
315 2144 : const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a));
316 4288 : const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
317 2144 : BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false);
318 2144 : aResultVector.push_back(pNew);
319 2412 : }
320 : }
321 : else
322 : {
323 : // fallback to bevel when there is not at least one segment hor and ver
324 9216 : aLineJoin = basegfx::B2DLINEJOIN_BEVEL;
325 : }
326 : }
327 :
328 9484 : if(basegfx::B2DLINEJOIN_MIDDLE == aLineJoin
329 9484 : || basegfx::B2DLINEJOIN_BEVEL == aLineJoin
330 268 : || basegfx::B2DLINEJOIN_MITER == aLineJoin)
331 : {
332 9216 : if(basegfx::B2DLINEJOIN_MITER == aLineJoin)
333 : {
334 0 : const double fMiterAngle(fAngle/2.0);
335 :
336 0 : if(fMiterAngle < fMiterMinimumAngle)
337 : {
338 : // fallback to bevel when miter's angle is too small
339 0 : aLineJoin = basegfx::B2DLINEJOIN_BEVEL;
340 : }
341 : }
342 :
343 9216 : const double fInc(F_PI / (double)nVerSeg);
344 9216 : const double fSin(sin(-fAngle));
345 9216 : const double fCos(cos(-fAngle));
346 9216 : const bool bMiter(basegfx::B2DLINEJOIN_MITER == aLineJoin);
347 9216 : const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0);
348 9216 : const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0);
349 9216 : double fPos(-F_PI2);
350 18432 : basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY;
351 18432 : basegfx::B3DPoint aCurrMiter, aNextMiter;
352 18432 : basegfx::B3DPolygon aNewPolygon, aMiterPolygon;
353 :
354 : // close polygon
355 9216 : aNewPolygon.setClosed(true);
356 9216 : aMiterPolygon.setClosed(true);
357 :
358 46080 : for(sal_uInt32 a(0L); a < nVerSeg; a++)
359 : {
360 36864 : const bool bFirst(0L == a);
361 36864 : const bool bLast(a + 1L == nVerSeg);
362 :
363 36864 : if(bFirst || !bLast)
364 : {
365 27648 : fPos += fInc;
366 :
367 55296 : aNextPointOnXY = basegfx::B3DPoint(
368 : cos(fPos),
369 : sin(fPos),
370 27648 : 0.0);
371 :
372 82944 : aNextPointRotY = basegfx::B3DPoint(
373 27648 : aNextPointOnXY.getX() * fCos,
374 : aNextPointOnXY.getY(),
375 55296 : aNextPointOnXY.getX() * fSin);
376 :
377 27648 : if(bMiter)
378 : {
379 0 : aNextMiter = basegfx::B3DPoint(
380 : aNextPointOnXY.getX(),
381 : aNextPointOnXY.getY(),
382 0 : fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
383 : }
384 : }
385 :
386 36864 : if(bFirst)
387 : {
388 9216 : aNewPolygon.clear();
389 :
390 9216 : if(bMiter)
391 : {
392 0 : aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
393 0 : aNewPolygon.append(aNextPointOnXY);
394 0 : aNewPolygon.append(aNextMiter);
395 :
396 0 : aMiterPolygon.clear();
397 0 : aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
398 0 : aMiterPolygon.append(aNextMiter);
399 0 : aMiterPolygon.append(aNextPointRotY);
400 : }
401 : else
402 : {
403 9216 : aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
404 9216 : aNewPolygon.append(aNextPointOnXY);
405 9216 : aNewPolygon.append(aNextPointRotY);
406 : }
407 : }
408 27648 : else if(bLast)
409 : {
410 9216 : aNewPolygon.clear();
411 :
412 9216 : if(bMiter)
413 : {
414 0 : aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
415 0 : aNewPolygon.append(aCurrMiter);
416 0 : aNewPolygon.append(aPointOnXY);
417 :
418 0 : aMiterPolygon.clear();
419 0 : aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
420 0 : aMiterPolygon.append(aPointRotY);
421 0 : aMiterPolygon.append(aCurrMiter);
422 : }
423 : else
424 : {
425 9216 : aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
426 9216 : aNewPolygon.append(aPointRotY);
427 9216 : aNewPolygon.append(aPointOnXY);
428 : }
429 : }
430 : else
431 : {
432 18432 : aNewPolygon.clear();
433 :
434 18432 : if(bMiter)
435 : {
436 0 : aNewPolygon.append(aPointOnXY);
437 0 : aNewPolygon.append(aNextPointOnXY);
438 0 : aNewPolygon.append(aNextMiter);
439 0 : aNewPolygon.append(aCurrMiter);
440 :
441 0 : aMiterPolygon.clear();
442 0 : aMiterPolygon.append(aCurrMiter);
443 0 : aMiterPolygon.append(aNextMiter);
444 0 : aMiterPolygon.append(aNextPointRotY);
445 0 : aMiterPolygon.append(aPointRotY);
446 : }
447 : else
448 : {
449 18432 : aNewPolygon.append(aPointRotY);
450 18432 : aNewPolygon.append(aPointOnXY);
451 18432 : aNewPolygon.append(aNextPointOnXY);
452 18432 : aNewPolygon.append(aNextPointRotY);
453 : }
454 : }
455 :
456 : // set normals
457 165888 : for(sal_uInt32 b(0L); b < aNewPolygon.count(); b++)
458 : {
459 129024 : aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
460 : }
461 :
462 : // create primitive
463 36864 : if(aNewPolygon.count())
464 : {
465 36864 : const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
466 36864 : BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false);
467 36864 : aResultVector.push_back(pNew);
468 : }
469 :
470 36864 : if(bMiter && aMiterPolygon.count())
471 : {
472 : // set normals
473 0 : for(sal_uInt32 c(0L); c < aMiterPolygon.count(); c++)
474 : {
475 0 : aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c)));
476 : }
477 :
478 : // create primitive
479 0 : const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
480 0 : BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false);
481 0 : aResultVector.push_back(pNew);
482 : }
483 :
484 : // prepare next step
485 36864 : if(bFirst || !bLast)
486 : {
487 27648 : aPointOnXY = aNextPointOnXY;
488 27648 : aPointRotY = aNextPointRotY;
489 :
490 27648 : if(bMiter)
491 : {
492 0 : aCurrMiter = aNextMiter;
493 : }
494 : }
495 9216 : }
496 : }
497 : }
498 :
499 9484 : Primitive3DSequence aRetval(aResultVector.size());
500 :
501 48492 : for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
502 : {
503 39008 : aRetval[a] = Primitive3DReference(aResultVector[a]);
504 : }
505 :
506 9484 : return aRetval;
507 : }
508 :
509 9902 : basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
510 : {
511 : // build transformation from unit vector to vector
512 9902 : basegfx::B3DHomMatrix aRetval;
513 :
514 : // get applied rotations from angles in XY and in XZ (cartesian)
515 9902 : const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength()));
516 9902 : const double fRotInXZ(atan2(-rVector.getZ(), rVector.getX()));
517 :
518 : // apply rotations. Rot around Z needs to be done first, so apply in two steps
519 9902 : aRetval.rotate(0.0, 0.0, fRotInXY);
520 9902 : aRetval.rotate(0.0, fRotInXZ, 0.0);
521 :
522 9902 : return aRetval;
523 : }
524 : } // end of anonymous namespace
525 : } // end of namespace primitive3d
526 : } // end of namespace drawinglayer
527 :
528 :
529 :
530 : using namespace com::sun::star;
531 :
532 :
533 :
534 : namespace drawinglayer
535 : {
536 : namespace primitive3d
537 : {
538 898 : Primitive3DSequence PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
539 : {
540 898 : const sal_uInt32 nPointCount(getB3DPolygon().count());
541 898 : std::vector< BasePrimitive3D* > aResultVector;
542 :
543 898 : if(nPointCount)
544 : {
545 898 : if(basegfx::fTools::more(getRadius(), 0.0))
546 : {
547 898 : const attribute::MaterialAttribute3D aMaterial(getBColor());
548 : static sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps)
549 898 : const bool bClosed(getB3DPolygon().isClosed());
550 898 : const bool bNoLineJoin(basegfx::B2DLINEJOIN_NONE == getLineJoin());
551 898 : const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1);
552 1796 : basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1));
553 1796 : basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0));
554 :
555 10992 : for(sal_uInt32 a(0); a < nLoopCount; a++)
556 : {
557 : // get next data
558 10094 : const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1) % nPointCount));
559 20188 : const basegfx::B3DVector aForw(aNext - aCurr);
560 10094 : const double fForwLen(aForw.getLength());
561 :
562 10094 : if(basegfx::fTools::more(fForwLen, 0.0))
563 : {
564 : // find out if linecap is active
565 9902 : const bool bFirst(!a);
566 9902 : const bool bLast(a + 1 == nLoopCount);
567 9902 : const bool bLineCapPossible(!bClosed && (bFirst || bLast));
568 9902 : const bool bLineCapRound(bLineCapPossible && com::sun::star::drawing::LineCap_ROUND == getLineCap());
569 9902 : const bool bLineCapSquare(bLineCapPossible && com::sun::star::drawing::LineCap_SQUARE == getLineCap());
570 :
571 : // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
572 9902 : basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
573 :
574 : // prepare transformations for tube and cap
575 19804 : basegfx::B3DHomMatrix aTubeTrans;
576 19804 : basegfx::B3DHomMatrix aCapTrans;
577 :
578 : // cap gets radius size
579 9902 : aCapTrans.scale(getRadius(), getRadius(), getRadius());
580 :
581 9902 : if(bLineCapSquare)
582 : {
583 : // when square line cap just prolong line segment in X, maybe 2 x radius when
584 : // first and last (simple line segment)
585 0 : const double fExtraLength(bFirst && bLast ? getRadius() * 2.0 : getRadius());
586 :
587 0 : aTubeTrans.scale(fForwLen + fExtraLength, getRadius(), getRadius());
588 :
589 0 : if(bFirst)
590 : {
591 : // correct start positions for tube and cap when first and square prolonged
592 0 : aTubeTrans.translate(-getRadius(), 0.0, 0.0);
593 0 : aCapTrans.translate(-getRadius(), 0.0, 0.0);
594 : }
595 : }
596 : else
597 : {
598 : // normal tube size
599 9902 : aTubeTrans.scale(fForwLen, getRadius(), getRadius());
600 : }
601 :
602 : // rotate and translate tube and cap
603 9902 : aTubeTrans *= aRotVector;
604 9902 : aTubeTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
605 9902 : aCapTrans *= aRotVector;
606 9902 : aCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
607 :
608 9902 : if(bNoLineJoin || (!bClosed && bFirst))
609 : {
610 : // line start edge, build transformed primitiveVector3D
611 418 : Primitive3DSequence aSequence;
612 :
613 418 : if(bLineCapRound && bFirst)
614 : {
615 : // LineCapRound used
616 0 : aSequence = getLineCapRoundSegments(nSegments, aMaterial);
617 : }
618 : else
619 : {
620 : // simple closing cap
621 418 : aSequence = getLineCapSegments(nSegments, aMaterial);
622 : }
623 :
624 418 : TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aCapTrans, aSequence);
625 418 : aResultVector.push_back(pNewTransformedA);
626 : }
627 : else
628 : {
629 9484 : const basegfx::B3DVector aBack(aCurr - aLast);
630 9484 : const double fCross(basegfx::cross(aBack, aForw).getLength());
631 :
632 9484 : if(!basegfx::fTools::equalZero(fCross))
633 : {
634 : // line connect non-parallel, aBack, aForw, use getLineJoin()
635 9484 : const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. F_PI2
636 : Primitive3DSequence aNewList(
637 : getLineJoinSegments(
638 : nSegments,
639 : aMaterial,
640 : fAngle,
641 : getDegreeStepWidth(),
642 : getMiterMinimumAngle(),
643 9484 : getLineJoin()));
644 :
645 : // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
646 18968 : basegfx::B3DHomMatrix aInvRotVector(aRotVector);
647 9484 : aInvRotVector.invert();
648 18968 : basegfx::B3DVector aTransBack(aInvRotVector * aBack);
649 9484 : const double fRotInYZ(atan2(aTransBack.getY(), aTransBack.getZ()));
650 :
651 : // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
652 : // Also apply usual scaling and translation
653 18968 : basegfx::B3DHomMatrix aSphereTrans;
654 9484 : aSphereTrans.rotate(0.0, F_PI2, 0.0);
655 9484 : aSphereTrans.rotate(F_PI - fRotInYZ, 0.0, 0.0);
656 9484 : aSphereTrans *= aRotVector;
657 9484 : aSphereTrans.scale(getRadius(), getRadius(), getRadius());
658 9484 : aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
659 :
660 : // line start edge, build transformed primitiveVector3D
661 : aResultVector.push_back(
662 : new TransformPrimitive3D(
663 : aSphereTrans,
664 18968 : aNewList));
665 9484 : }
666 : }
667 :
668 : // create line segments, build transformed primitiveVector3D
669 : aResultVector.push_back(
670 : new TransformPrimitive3D(
671 : aTubeTrans,
672 9902 : getLineTubeSegments(nSegments, aMaterial)));
673 :
674 9902 : if(bNoLineJoin || (!bClosed && bLast))
675 : {
676 : // line end edge
677 418 : basegfx::B3DHomMatrix aBackCapTrans;
678 :
679 : // Mirror (line end) and radius scale
680 418 : aBackCapTrans.rotate(0.0, F_PI, 0.0);
681 418 : aBackCapTrans.scale(getRadius(), getRadius(), getRadius());
682 :
683 418 : if(bLineCapSquare && bLast)
684 : {
685 : // correct position when square and prolonged
686 0 : aBackCapTrans.translate(fForwLen + getRadius(), 0.0, 0.0);
687 : }
688 : else
689 : {
690 : // standard position
691 418 : aBackCapTrans.translate(fForwLen, 0.0, 0.0);
692 : }
693 :
694 : // rotate and translate to destination
695 418 : aBackCapTrans *= aRotVector;
696 418 : aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
697 :
698 : // get primitiveVector3D
699 836 : Primitive3DSequence aSequence;
700 :
701 418 : if(bLineCapRound && bLast)
702 : {
703 : // LineCapRound used
704 0 : aSequence = getLineCapRoundSegments(nSegments, aMaterial);
705 : }
706 : else
707 : {
708 : // simple closing cap
709 418 : aSequence = getLineCapSegments(nSegments, aMaterial);
710 : }
711 :
712 : aResultVector.push_back(
713 : new TransformPrimitive3D(
714 : aBackCapTrans,
715 836 : aSequence));
716 9902 : }
717 : }
718 :
719 : // prepare next loop step
720 10094 : aLast = aCurr;
721 10094 : aCurr = aNext;
722 10992 : }
723 : }
724 : else
725 : {
726 : // create hairline
727 0 : PolygonHairlinePrimitive3D* pNew = new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor());
728 0 : aResultVector.push_back(pNew);
729 : }
730 : }
731 :
732 : // prepare return value
733 898 : Primitive3DSequence aRetval(aResultVector.size());
734 :
735 21120 : for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
736 : {
737 20222 : aRetval[a] = Primitive3DReference(aResultVector[a]);
738 : }
739 :
740 898 : return aRetval;
741 : }
742 :
743 898 : PolygonTubePrimitive3D::PolygonTubePrimitive3D(
744 : const basegfx::B3DPolygon& rPolygon,
745 : const basegfx::BColor& rBColor,
746 : double fRadius, basegfx::B2DLineJoin aLineJoin,
747 : com::sun::star::drawing::LineCap aLineCap,
748 : double fDegreeStepWidth,
749 : double fMiterMinimumAngle)
750 : : PolygonHairlinePrimitive3D(rPolygon, rBColor),
751 : maLast3DDecomposition(),
752 : mfRadius(fRadius),
753 : mfDegreeStepWidth(fDegreeStepWidth),
754 : mfMiterMinimumAngle(fMiterMinimumAngle),
755 : maLineJoin(aLineJoin),
756 898 : maLineCap(aLineCap)
757 : {
758 898 : }
759 :
760 0 : bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
761 : {
762 0 : if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
763 : {
764 0 : const PolygonTubePrimitive3D& rCompare = static_cast<const PolygonTubePrimitive3D&>(rPrimitive);
765 :
766 0 : return (getRadius() == rCompare.getRadius()
767 0 : && getDegreeStepWidth() == rCompare.getDegreeStepWidth()
768 0 : && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle()
769 0 : && getLineJoin() == rCompare.getLineJoin()
770 0 : && getLineCap() == rCompare.getLineCap());
771 : }
772 :
773 0 : return false;
774 : }
775 :
776 1098 : Primitive3DSequence PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
777 : {
778 1098 : ::osl::MutexGuard aGuard( m_aMutex );
779 :
780 1098 : if(!getLast3DDecomposition().hasElements())
781 : {
782 898 : const Primitive3DSequence aNewSequence(impCreate3DDecomposition(rViewInformation));
783 898 : const_cast< PolygonTubePrimitive3D* >(this)->setLast3DDecomposition(aNewSequence);
784 : }
785 :
786 1098 : return getLast3DDecomposition();
787 : }
788 :
789 : // provide unique ID
790 1098 : ImplPrimitive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D)
791 :
792 : } // end of namespace primitive3d
793 : } // end of namespace drawinglayer
794 :
795 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|