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 0 : 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 0 : TubeBuffer()
49 0 : : m_nLineTubeSegments(0L)
50 : {
51 0 : }
52 :
53 0 : Primitive3DSequence getLineTubeSegments(
54 : sal_uInt32 nSegments,
55 : const attribute::MaterialAttribute3D& rMaterial)
56 : {
57 : // may exclusively change cached data, use mutex
58 0 : ::osl::MutexGuard aGuard(m_aMutex);
59 :
60 0 : if (nSegments != m_nLineTubeSegments || !(rMaterial == m_aLineMaterial))
61 : {
62 0 : m_nLineTubeSegments = nSegments;
63 0 : m_aLineMaterial = rMaterial;
64 0 : m_aLineTubeList = Primitive3DSequence();
65 : }
66 :
67 0 : if (!m_aLineTubeList.hasElements() && m_nLineTubeSegments != 0)
68 : {
69 0 : const basegfx::B3DPoint aLeft(0.0, 0.0, 0.0);
70 0 : const basegfx::B3DPoint aRight(1.0, 0.0, 0.0);
71 0 : basegfx::B3DPoint aLastLeft(0.0, 1.0, 0.0);
72 0 : basegfx::B3DPoint aLastRight(1.0, 1.0, 0.0);
73 0 : basegfx::B3DHomMatrix aRot;
74 0 : aRot.rotate(F_2PI / (double)m_nLineTubeSegments, 0.0, 0.0);
75 0 : m_aLineTubeList.realloc(m_nLineTubeSegments);
76 :
77 0 : for(sal_uInt32 a = 0; a < m_nLineTubeSegments; ++a)
78 : {
79 0 : const basegfx::B3DPoint aNextLeft(aRot * aLastLeft);
80 0 : const basegfx::B3DPoint aNextRight(aRot * aLastRight);
81 0 : basegfx::B3DPolygon aNewPolygon;
82 :
83 0 : aNewPolygon.append(aNextLeft);
84 0 : aNewPolygon.setNormal(0L, basegfx::B3DVector(aNextLeft - aLeft));
85 :
86 0 : aNewPolygon.append(aLastLeft);
87 0 : aNewPolygon.setNormal(1L, basegfx::B3DVector(aLastLeft - aLeft));
88 :
89 0 : aNewPolygon.append(aLastRight);
90 0 : aNewPolygon.setNormal(2L, basegfx::B3DVector(aLastRight - aRight));
91 :
92 0 : aNewPolygon.append(aNextRight);
93 0 : aNewPolygon.setNormal(3L, basegfx::B3DVector(aNextRight - aRight));
94 :
95 0 : aNewPolygon.setClosed(true);
96 :
97 0 : const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
98 0 : const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
99 0 : m_aLineTubeList[a] = xRef;
100 :
101 0 : aLastLeft = aNextLeft;
102 0 : aLastRight = aNextRight;
103 0 : }
104 : }
105 0 : return m_aLineTubeList;
106 : }
107 : };
108 :
109 : struct theTubeBuffer :
110 : public rtl::Static< TubeBuffer, theTubeBuffer > {};
111 :
112 0 : Primitive3DSequence getLineTubeSegments(
113 : sal_uInt32 nSegments,
114 : const attribute::MaterialAttribute3D& rMaterial)
115 : {
116 : // static data for buffered tube primitives
117 0 : TubeBuffer &rTheBuffer = theTubeBuffer::get();
118 0 : return rTheBuffer.getLineTubeSegments(nSegments, rMaterial);
119 : }
120 :
121 0 : 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 0 : CapBuffer()
131 0 : : m_nLineCapSegments(0)
132 : {
133 0 : }
134 0 : Primitive3DSequence getLineCapSegments(
135 : sal_uInt32 nSegments,
136 : const attribute::MaterialAttribute3D& rMaterial)
137 : {
138 : // may exclusively change cached data, use mutex
139 0 : ::osl::MutexGuard aGuard(m_aMutex);
140 :
141 0 : if (nSegments != m_nLineCapSegments || !(rMaterial == m_aLineMaterial))
142 : {
143 0 : m_nLineCapSegments = nSegments;
144 0 : m_aLineMaterial = rMaterial;
145 0 : m_aLineCapList = Primitive3DSequence();
146 : }
147 :
148 0 : if (!m_aLineCapList.hasElements() && m_nLineCapSegments != 0)
149 : {
150 0 : const basegfx::B3DPoint aNull(0.0, 0.0, 0.0);
151 0 : basegfx::B3DPoint aLast(0.0, 1.0, 0.0);
152 0 : basegfx::B3DHomMatrix aRot;
153 0 : aRot.rotate(F_2PI / (double)m_nLineCapSegments, 0.0, 0.0);
154 0 : m_aLineCapList.realloc(m_nLineCapSegments);
155 :
156 0 : for(sal_uInt32 a = 0; a < m_nLineCapSegments; ++a)
157 : {
158 0 : const basegfx::B3DPoint aNext(aRot * aLast);
159 0 : basegfx::B3DPolygon aNewPolygon;
160 :
161 0 : aNewPolygon.append(aLast);
162 0 : aNewPolygon.setNormal(0L, basegfx::B3DVector(aLast - aNull));
163 :
164 0 : aNewPolygon.append(aNext);
165 0 : aNewPolygon.setNormal(1L, basegfx::B3DVector(aNext - aNull));
166 :
167 0 : aNewPolygon.append(aNull);
168 0 : aNewPolygon.setNormal(2L, basegfx::B3DVector(-1.0, 0.0, 0.0));
169 :
170 0 : aNewPolygon.setClosed(true);
171 :
172 0 : const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
173 0 : const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
174 0 : m_aLineCapList[a] = xRef;
175 :
176 0 : aLast = aNext;
177 0 : }
178 : }
179 :
180 0 : return m_aLineCapList;
181 : }
182 : };
183 :
184 : struct theCapBuffer :
185 : public rtl::Static< CapBuffer, theCapBuffer > {};
186 :
187 0 : Primitive3DSequence getLineCapSegments(
188 : sal_uInt32 nSegments,
189 : const attribute::MaterialAttribute3D& rMaterial)
190 : {
191 : // static data for buffered cap primitives
192 0 : CapBuffer &rTheBuffer = theCapBuffer::get();
193 0 : 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 0 : 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 0 : const sal_uInt32 nVerSeg(nSegments >> 1L);
299 0 : std::vector< BasePrimitive3D* > aResultVector;
300 :
301 0 : if(nVerSeg)
302 : {
303 0 : if(basegfx::B2DLINEJOIN_ROUND == aLineJoin)
304 : {
305 : // calculate new horizontal segments
306 0 : const sal_uInt32 nHorSeg(basegfx::fround((fAngle / F_2PI) * (double)nSegments));
307 :
308 0 : if(nHorSeg)
309 : {
310 : // create half-sphere
311 0 : const basegfx::B3DPolyPolygon aSphere(basegfx::tools::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle));
312 :
313 0 : for(sal_uInt32 a(0L); a < aSphere.count(); a++)
314 : {
315 0 : const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a));
316 0 : const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
317 0 : BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false);
318 0 : aResultVector.push_back(pNew);
319 0 : }
320 : }
321 : else
322 : {
323 : // fallback to bevel when there is not at least one segment hor and ver
324 0 : aLineJoin = basegfx::B2DLINEJOIN_BEVEL;
325 : }
326 : }
327 :
328 0 : if(basegfx::B2DLINEJOIN_MIDDLE == aLineJoin
329 0 : || basegfx::B2DLINEJOIN_BEVEL == aLineJoin
330 0 : || basegfx::B2DLINEJOIN_MITER == aLineJoin)
331 : {
332 0 : 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 0 : const double fInc(F_PI / (double)nVerSeg);
344 0 : const double fSin(sin(-fAngle));
345 0 : const double fCos(cos(-fAngle));
346 0 : const bool bMiter(basegfx::B2DLINEJOIN_MITER == aLineJoin);
347 0 : const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0);
348 0 : const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0);
349 0 : double fPos(-F_PI2);
350 0 : basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY;
351 0 : basegfx::B3DPoint aCurrMiter, aNextMiter;
352 0 : basegfx::B3DPolygon aNewPolygon, aMiterPolygon;
353 :
354 : // close polygon
355 0 : aNewPolygon.setClosed(true);
356 0 : aMiterPolygon.setClosed(true);
357 :
358 0 : for(sal_uInt32 a(0L); a < nVerSeg; a++)
359 : {
360 0 : const bool bFirst(0L == a);
361 0 : const bool bLast(a + 1L == nVerSeg);
362 :
363 0 : if(bFirst || !bLast)
364 : {
365 0 : fPos += fInc;
366 :
367 0 : aNextPointOnXY = basegfx::B3DPoint(
368 : cos(fPos),
369 : sin(fPos),
370 0 : 0.0);
371 :
372 0 : aNextPointRotY = basegfx::B3DPoint(
373 0 : aNextPointOnXY.getX() * fCos,
374 : aNextPointOnXY.getY(),
375 0 : aNextPointOnXY.getX() * fSin);
376 :
377 0 : if(bMiter)
378 : {
379 0 : aNextMiter = basegfx::B3DPoint(
380 : aNextPointOnXY.getX(),
381 : aNextPointOnXY.getY(),
382 0 : fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
383 : }
384 : }
385 :
386 0 : if(bFirst)
387 : {
388 0 : aNewPolygon.clear();
389 :
390 0 : 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 0 : aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
404 0 : aNewPolygon.append(aNextPointOnXY);
405 0 : aNewPolygon.append(aNextPointRotY);
406 : }
407 : }
408 0 : else if(bLast)
409 : {
410 0 : aNewPolygon.clear();
411 :
412 0 : 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 0 : aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
426 0 : aNewPolygon.append(aPointRotY);
427 0 : aNewPolygon.append(aPointOnXY);
428 : }
429 : }
430 : else
431 : {
432 0 : aNewPolygon.clear();
433 :
434 0 : 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 0 : aNewPolygon.append(aPointRotY);
450 0 : aNewPolygon.append(aPointOnXY);
451 0 : aNewPolygon.append(aNextPointOnXY);
452 0 : aNewPolygon.append(aNextPointRotY);
453 : }
454 : }
455 :
456 : // set normals
457 0 : for(sal_uInt32 b(0L); b < aNewPolygon.count(); b++)
458 : {
459 0 : aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
460 : }
461 :
462 : // create primitive
463 0 : if(aNewPolygon.count())
464 : {
465 0 : const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
466 0 : BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false);
467 0 : aResultVector.push_back(pNew);
468 : }
469 :
470 0 : 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 0 : if(bFirst || !bLast)
486 : {
487 0 : aPointOnXY = aNextPointOnXY;
488 0 : aPointRotY = aNextPointRotY;
489 :
490 0 : if(bMiter)
491 : {
492 0 : aCurrMiter = aNextMiter;
493 : }
494 : }
495 0 : }
496 : }
497 : }
498 :
499 0 : Primitive3DSequence aRetval(aResultVector.size());
500 :
501 0 : for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
502 : {
503 0 : aRetval[a] = Primitive3DReference(aResultVector[a]);
504 : }
505 :
506 0 : return aRetval;
507 : }
508 :
509 0 : basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
510 : {
511 : // build transformation from unit vector to vector
512 0 : basegfx::B3DHomMatrix aRetval;
513 :
514 : // get applied rotations from angles in XY and in XZ (cartesian)
515 0 : const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength()));
516 0 : 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 0 : aRetval.rotate(0.0, 0.0, fRotInXY);
520 0 : aRetval.rotate(0.0, fRotInXZ, 0.0);
521 :
522 0 : 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 0 : Primitive3DSequence PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
539 : {
540 0 : const sal_uInt32 nPointCount(getB3DPolygon().count());
541 0 : std::vector< BasePrimitive3D* > aResultVector;
542 :
543 0 : if(nPointCount)
544 : {
545 0 : if(basegfx::fTools::more(getRadius(), 0.0))
546 : {
547 0 : 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 0 : const bool bClosed(getB3DPolygon().isClosed());
550 0 : const bool bNoLineJoin(basegfx::B2DLINEJOIN_NONE == getLineJoin());
551 0 : const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1);
552 0 : basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1));
553 0 : basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0));
554 :
555 0 : for(sal_uInt32 a(0); a < nLoopCount; a++)
556 : {
557 : // get next data
558 0 : const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1) % nPointCount));
559 0 : const basegfx::B3DVector aForw(aNext - aCurr);
560 0 : const double fForwLen(aForw.getLength());
561 :
562 0 : if(basegfx::fTools::more(fForwLen, 0.0))
563 : {
564 : // find out if linecap is active
565 0 : const bool bFirst(!a);
566 0 : const bool bLast(a + 1 == nLoopCount);
567 0 : const bool bLineCapPossible(!bClosed && (bFirst || bLast));
568 0 : const bool bLineCapRound(bLineCapPossible && com::sun::star::drawing::LineCap_ROUND == getLineCap());
569 0 : 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 0 : basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
573 :
574 : // prepare transformations for tube and cap
575 0 : basegfx::B3DHomMatrix aTubeTrans;
576 0 : basegfx::B3DHomMatrix aCapTrans;
577 :
578 : // cap gets radius size
579 0 : aCapTrans.scale(getRadius(), getRadius(), getRadius());
580 :
581 0 : 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 0 : aTubeTrans.scale(fForwLen, getRadius(), getRadius());
600 : }
601 :
602 : // rotate and translate tube and cap
603 0 : aTubeTrans *= aRotVector;
604 0 : aTubeTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
605 0 : aCapTrans *= aRotVector;
606 0 : aCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
607 :
608 0 : if(bNoLineJoin || (!bClosed && bFirst))
609 : {
610 : // line start edge, build transformed primitiveVector3D
611 0 : Primitive3DSequence aSequence;
612 :
613 0 : if(bLineCapRound && bFirst)
614 : {
615 : // LineCapRound used
616 0 : aSequence = getLineCapRoundSegments(nSegments, aMaterial);
617 : }
618 : else
619 : {
620 : // simple closing cap
621 0 : aSequence = getLineCapSegments(nSegments, aMaterial);
622 : }
623 :
624 0 : TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aCapTrans, aSequence);
625 0 : aResultVector.push_back(pNewTransformedA);
626 : }
627 : else
628 : {
629 0 : const basegfx::B3DVector aBack(aCurr - aLast);
630 0 : const double fCross(basegfx::cross(aBack, aForw).getLength());
631 :
632 0 : if(!basegfx::fTools::equalZero(fCross))
633 : {
634 : // line connect non-parallel, aBack, aForw, use getLineJoin()
635 0 : 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 0 : getLineJoin()));
644 :
645 : // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
646 0 : basegfx::B3DHomMatrix aInvRotVector(aRotVector);
647 0 : aInvRotVector.invert();
648 0 : basegfx::B3DVector aTransBack(aInvRotVector * aBack);
649 0 : 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 0 : basegfx::B3DHomMatrix aSphereTrans;
654 0 : aSphereTrans.rotate(0.0, F_PI2, 0.0);
655 0 : aSphereTrans.rotate(F_PI - fRotInYZ, 0.0, 0.0);
656 0 : aSphereTrans *= aRotVector;
657 0 : aSphereTrans.scale(getRadius(), getRadius(), getRadius());
658 0 : 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 0 : aNewList));
665 0 : }
666 : }
667 :
668 : // create line segments, build transformed primitiveVector3D
669 : aResultVector.push_back(
670 : new TransformPrimitive3D(
671 : aTubeTrans,
672 0 : getLineTubeSegments(nSegments, aMaterial)));
673 :
674 0 : if(bNoLineJoin || (!bClosed && bLast))
675 : {
676 : // line end edge
677 0 : basegfx::B3DHomMatrix aBackCapTrans;
678 :
679 : // Mirror (line end) and radius scale
680 0 : aBackCapTrans.rotate(0.0, F_PI, 0.0);
681 0 : aBackCapTrans.scale(getRadius(), getRadius(), getRadius());
682 :
683 0 : 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 0 : aBackCapTrans.translate(fForwLen, 0.0, 0.0);
692 : }
693 :
694 : // rotate and translate to destination
695 0 : aBackCapTrans *= aRotVector;
696 0 : aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
697 :
698 : // get primitiveVector3D
699 0 : Primitive3DSequence aSequence;
700 :
701 0 : if(bLineCapRound && bLast)
702 : {
703 : // LineCapRound used
704 0 : aSequence = getLineCapRoundSegments(nSegments, aMaterial);
705 : }
706 : else
707 : {
708 : // simple closing cap
709 0 : aSequence = getLineCapSegments(nSegments, aMaterial);
710 : }
711 :
712 : aResultVector.push_back(
713 : new TransformPrimitive3D(
714 : aBackCapTrans,
715 0 : aSequence));
716 0 : }
717 : }
718 :
719 : // prepare next loop step
720 0 : aLast = aCurr;
721 0 : aCurr = aNext;
722 0 : }
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 0 : Primitive3DSequence aRetval(aResultVector.size());
734 :
735 0 : for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
736 : {
737 0 : aRetval[a] = Primitive3DReference(aResultVector[a]);
738 : }
739 :
740 0 : return aRetval;
741 : }
742 :
743 0 : 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 0 : maLineCap(aLineCap)
757 : {
758 0 : }
759 :
760 0 : bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
761 : {
762 0 : if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
763 : {
764 0 : const PolygonTubePrimitive3D& rCompare = (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 0 : Primitive3DSequence PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
777 : {
778 0 : ::osl::MutexGuard aGuard( m_aMutex );
779 :
780 0 : if(!getLast3DDecomposition().hasElements())
781 : {
782 0 : const Primitive3DSequence aNewSequence(impCreate3DDecomposition(rViewInformation));
783 0 : const_cast< PolygonTubePrimitive3D* >(this)->setLast3DDecomposition(aNewSequence);
784 : }
785 :
786 0 : return getLast3DDecomposition();
787 : }
788 :
789 : // provide unique ID
790 0 : 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: */
|