Bump for 3.6-28
[LibreOffice.git] / drawinglayer / source / primitive3d / polygontubeprimitive3d.cxx
blobfee2d0f9622e1ce6377ab2b6b43a7b04b4e72935
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #include <drawinglayer/primitive3d/polygontubeprimitive3d.hxx>
30 #include <drawinglayer/attribute/materialattribute3d.hxx>
31 #include <basegfx/matrix/b3dhommatrix.hxx>
32 #include <basegfx/polygon/b3dpolypolygon.hxx>
33 #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
34 #include <basegfx/polygon/b3dpolypolygontools.hxx>
35 #include <drawinglayer/primitive3d/transformprimitive3d.hxx>
36 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
38 //////////////////////////////////////////////////////////////////////////////
40 namespace drawinglayer
42 namespace primitive3d
44 namespace // anonymous namespace
46 Primitive3DSequence getLineTubeSegments(
47 sal_uInt32 nSegments,
48 const attribute::MaterialAttribute3D& rMaterial)
50 // static data for buffered tube primitives
51 static Primitive3DSequence aLineTubeList;
52 static sal_uInt32 nLineTubeSegments(0L);
53 static attribute::MaterialAttribute3D aLineMaterial;
55 // may exclusively change static data, use mutex
56 ::osl::Mutex m_mutex;
58 if(nSegments != nLineTubeSegments || !(rMaterial == aLineMaterial))
60 nLineTubeSegments = nSegments;
61 aLineMaterial = rMaterial;
62 aLineTubeList = Primitive3DSequence();
65 if(!aLineTubeList.hasElements() && 0L != nLineTubeSegments)
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(F_2PI / (double)nLineTubeSegments, 0.0, 0.0);
73 aLineTubeList.realloc(nLineTubeSegments);
75 for(sal_uInt32 a(0L); a < 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(0L, basegfx::B3DVector(aNextLeft - aLeft));
84 aNewPolygon.append(aLastLeft);
85 aNewPolygon.setNormal(1L, basegfx::B3DVector(aLastLeft - aLeft));
87 aNewPolygon.append(aLastRight);
88 aNewPolygon.setNormal(2L, basegfx::B3DVector(aLastRight - aRight));
90 aNewPolygon.append(aNextRight);
91 aNewPolygon.setNormal(3L, basegfx::B3DVector(aNextRight - aRight));
93 aNewPolygon.setClosed(true);
95 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
96 const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, aLineMaterial, false));
97 aLineTubeList[a] = xRef;
99 aLastLeft = aNextLeft;
100 aLastRight = aNextRight;
104 return aLineTubeList;
107 Primitive3DSequence getLineCapSegments(
108 sal_uInt32 nSegments,
109 const attribute::MaterialAttribute3D& rMaterial)
111 // static data for buffered tube primitives
112 static Primitive3DSequence aLineCapList;
113 static sal_uInt32 nLineCapSegments(0L);
114 static attribute::MaterialAttribute3D aLineMaterial;
116 // may exclusively change static data, use mutex
117 ::osl::Mutex m_mutex;
119 if(nSegments != nLineCapSegments || !(rMaterial == aLineMaterial))
121 nLineCapSegments = nSegments;
122 aLineMaterial = rMaterial;
123 aLineCapList = Primitive3DSequence();
126 if(!aLineCapList.hasElements() && 0L != nLineCapSegments)
128 const basegfx::B3DPoint aNull(0.0, 0.0, 0.0);
129 basegfx::B3DPoint aLast(0.0, 1.0, 0.0);
130 basegfx::B3DHomMatrix aRot;
131 aRot.rotate(F_2PI / (double)nLineCapSegments, 0.0, 0.0);
132 aLineCapList.realloc(nLineCapSegments);
134 for(sal_uInt32 a(0L); a < nLineCapSegments; a++)
136 const basegfx::B3DPoint aNext(aRot * aLast);
137 basegfx::B3DPolygon aNewPolygon;
139 aNewPolygon.append(aLast);
140 aNewPolygon.setNormal(0L, basegfx::B3DVector(aLast - aNull));
142 aNewPolygon.append(aNext);
143 aNewPolygon.setNormal(1L, basegfx::B3DVector(aNext - aNull));
145 aNewPolygon.append(aNull);
146 aNewPolygon.setNormal(2L, basegfx::B3DVector(-1.0, 0.0, 0.0));
148 aNewPolygon.setClosed(true);
150 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
151 const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, aLineMaterial, false));
152 aLineCapList[a] = xRef;
154 aLast = aNext;
158 return aLineCapList;
161 Primitive3DSequence getLineJoinSegments(
162 sal_uInt32 nSegments,
163 const attribute::MaterialAttribute3D& rMaterial,
164 double fAngle,
165 double /*fDegreeStepWidth*/,
166 double fMiterMinimumAngle,
167 basegfx::B2DLineJoin aLineJoin)
169 // nSegments is for whole circle, adapt to half circle
170 const sal_uInt32 nVerSeg(nSegments >> 1L);
171 std::vector< BasePrimitive3D* > aResultVector;
173 if(nVerSeg)
175 if(basegfx::B2DLINEJOIN_ROUND == aLineJoin)
177 // calculate new horizontal segments
178 const sal_uInt32 nHorSeg((sal_uInt32)((fAngle / F_2PI) * (double)nSegments));
180 if(nHorSeg)
182 // create half-sphere
183 const basegfx::B3DPolyPolygon aSphere(basegfx::tools::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle));
185 for(sal_uInt32 a(0L); a < aSphere.count(); a++)
187 const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a));
188 const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
189 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false);
190 aResultVector.push_back(pNew);
193 else
195 // fallback to bevel when there is not at least one segment hor and ver
196 aLineJoin = basegfx::B2DLINEJOIN_BEVEL;
200 if(basegfx::B2DLINEJOIN_MIDDLE == aLineJoin
201 || basegfx::B2DLINEJOIN_BEVEL == aLineJoin
202 || basegfx::B2DLINEJOIN_MITER == aLineJoin)
204 if(basegfx::B2DLINEJOIN_MITER == aLineJoin)
206 const double fMiterAngle(fAngle/2.0);
208 if(fMiterAngle < fMiterMinimumAngle)
210 // fallback to bevel when miter's angle is too small
211 aLineJoin = basegfx::B2DLINEJOIN_BEVEL;
215 const double fInc(F_PI / (double)nVerSeg);
216 const double fSin(sin(-fAngle));
217 const double fCos(cos(-fAngle));
218 const bool bMiter(basegfx::B2DLINEJOIN_MITER == aLineJoin);
219 const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0);
220 const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0);
221 double fPos(-F_PI2);
222 basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY;
223 basegfx::B3DPoint aCurrMiter, aNextMiter;
224 basegfx::B3DPolygon aNewPolygon, aMiterPolygon;
226 // close polygon
227 aNewPolygon.setClosed(true);
228 aMiterPolygon.setClosed(true);
230 for(sal_uInt32 a(0L); a < nVerSeg; a++)
232 const bool bFirst(0L == a);
233 const bool bLast(a + 1L == nVerSeg);
235 if(bFirst || !bLast)
237 fPos += fInc;
239 aNextPointOnXY = basegfx::B3DPoint(
240 cos(fPos),
241 sin(fPos),
242 0.0);
244 aNextPointRotY = basegfx::B3DPoint(
245 aNextPointOnXY.getX() * fCos,
246 aNextPointOnXY.getY(),
247 aNextPointOnXY.getX() * fSin);
249 if(bMiter)
251 aNextMiter = basegfx::B3DPoint(
252 aNextPointOnXY.getX(),
253 aNextPointOnXY.getY(),
254 fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
258 if(bFirst)
260 aNewPolygon.clear();
262 if(bMiter)
264 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
265 aNewPolygon.append(aNextPointOnXY);
266 aNewPolygon.append(aNextMiter);
268 aMiterPolygon.clear();
269 aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
270 aMiterPolygon.append(aNextMiter);
271 aMiterPolygon.append(aNextPointRotY);
273 else
275 aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
276 aNewPolygon.append(aNextPointOnXY);
277 aNewPolygon.append(aNextPointRotY);
280 else if(bLast)
282 aNewPolygon.clear();
284 if(bMiter)
286 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
287 aNewPolygon.append(aCurrMiter);
288 aNewPolygon.append(aPointOnXY);
290 aMiterPolygon.clear();
291 aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
292 aMiterPolygon.append(aPointRotY);
293 aMiterPolygon.append(aCurrMiter);
295 else
297 aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
298 aNewPolygon.append(aPointRotY);
299 aNewPolygon.append(aPointOnXY);
302 else
304 aNewPolygon.clear();
306 if(bMiter)
308 aNewPolygon.append(aPointOnXY);
309 aNewPolygon.append(aNextPointOnXY);
310 aNewPolygon.append(aNextMiter);
311 aNewPolygon.append(aCurrMiter);
313 aMiterPolygon.clear();
314 aMiterPolygon.append(aCurrMiter);
315 aMiterPolygon.append(aNextMiter);
316 aMiterPolygon.append(aNextPointRotY);
317 aMiterPolygon.append(aPointRotY);
319 else
321 aNewPolygon.append(aPointRotY);
322 aNewPolygon.append(aPointOnXY);
323 aNewPolygon.append(aNextPointOnXY);
324 aNewPolygon.append(aNextPointRotY);
328 // set normals
329 for(sal_uInt32 b(0L); b < aNewPolygon.count(); b++)
331 aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
334 // create primitive
335 if(aNewPolygon.count())
337 const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
338 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false);
339 aResultVector.push_back(pNew);
342 if(bMiter && aMiterPolygon.count())
344 // set normals
345 for(sal_uInt32 c(0L); c < aMiterPolygon.count(); c++)
347 aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c)));
350 // create primitive
351 const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
352 BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false);
353 aResultVector.push_back(pNew);
356 // prepare next step
357 if(bFirst || !bLast)
359 aPointOnXY = aNextPointOnXY;
360 aPointRotY = aNextPointRotY;
362 if(bMiter)
364 aCurrMiter = aNextMiter;
371 Primitive3DSequence aRetval(aResultVector.size());
373 for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
375 aRetval[a] = Primitive3DReference(aResultVector[a]);
378 return aRetval;
381 basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
383 // build transformation from unit vector to vector
384 basegfx::B3DHomMatrix aRetval;
386 // get applied rotations from angles in XY and in XZ (cartesian)
387 const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength()));
388 const double fRotInXZ(atan2(-rVector.getZ(), rVector.getX()));
390 // apply rotations. Rot around Z needs to be done first, so apply in two steps
391 aRetval.rotate(0.0, 0.0, fRotInXY);
392 aRetval.rotate(0.0, fRotInXZ, 0.0);
394 return aRetval;
396 } // end of anonymous namespace
397 } // end of namespace primitive3d
398 } // end of namespace drawinglayer
400 //////////////////////////////////////////////////////////////////////////////
402 using namespace com::sun::star;
404 //////////////////////////////////////////////////////////////////////////////
406 namespace drawinglayer
408 namespace primitive3d
410 Primitive3DSequence PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
412 const sal_uInt32 nPointCount(getB3DPolygon().count());
413 std::vector< BasePrimitive3D* > aResultVector;
415 if(0L != nPointCount)
417 if(basegfx::fTools::more(getRadius(), 0.0))
419 const attribute::MaterialAttribute3D aMaterial(getBColor());
420 static sal_uInt32 nSegments(8L); // default for 3d line segments, for more quality just raise this value (in even steps)
421 const bool bClosed(getB3DPolygon().isClosed());
422 const bool bNoLineJoin(basegfx::B2DLINEJOIN_NONE == getLineJoin());
423 const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1L);
424 basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1L));
425 basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0L));
427 for(sal_uInt32 a(0L); a < nLoopCount; a++)
429 // get next data
430 const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1L) % nPointCount));
431 const basegfx::B3DVector aForw(aNext - aCurr);
432 const double fForwLen(aForw.getLength());
434 if(basegfx::fTools::more(fForwLen, 0.0))
436 // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
437 basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
439 // create default transformation with scale and rotate
440 basegfx::B3DHomMatrix aVectorTrans;
441 aVectorTrans.scale(fForwLen, getRadius(), getRadius());
442 aVectorTrans *= aRotVector;
443 aVectorTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
445 if(bNoLineJoin || (!bClosed && !a))
447 // line start edge, build transformed primitiveVector3D
448 TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aVectorTrans, getLineCapSegments(nSegments, aMaterial));
449 aResultVector.push_back(pNewTransformedA);
451 else
453 const basegfx::B3DVector aBack(aCurr - aLast);
454 const double fCross(basegfx::cross(aBack, aForw).getLength());
456 if(!basegfx::fTools::equalZero(fCross))
458 // line connect non-parallel, aBack, aForw, use getLineJoin()
459 const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. F_PI2
460 Primitive3DSequence aNewList(getLineJoinSegments(nSegments, aMaterial, fAngle, getDegreeStepWidth(), getMiterMinimumAngle(), getLineJoin()));
462 // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
463 basegfx::B3DHomMatrix aInvRotVector(aRotVector);
464 aInvRotVector.invert();
465 basegfx::B3DVector aTransBack(aInvRotVector * aBack);
466 const double fRotInYZ(atan2(aTransBack.getY(), aTransBack.getZ()));
468 // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
469 // Also apply usual scaling and translation
470 basegfx::B3DHomMatrix aSphereTrans;
471 aSphereTrans.rotate(0.0, F_PI2, 0.0);
472 aSphereTrans.rotate(F_PI - fRotInYZ, 0.0, 0.0);
473 aSphereTrans *= aRotVector;
474 aSphereTrans.scale(getRadius(), getRadius(), getRadius());
475 aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
477 // line start edge, build transformed primitiveVector3D
478 TransformPrimitive3D* pNewTransformedB = new TransformPrimitive3D(aSphereTrans, aNewList);
479 aResultVector.push_back(pNewTransformedB);
483 // create line segments, build transformed primitiveVector3D
484 TransformPrimitive3D* pNewTransformedC = new TransformPrimitive3D(aVectorTrans, getLineTubeSegments(nSegments, aMaterial));
485 aResultVector.push_back(pNewTransformedC);
487 if(bNoLineJoin || (!bClosed && ((a + 1L) == nLoopCount)))
489 // line end edge, first rotate (mirror) and translate, then use use aRotVector
490 basegfx::B3DHomMatrix aBackTrans;
491 aBackTrans.rotate(0.0, F_PI, 0.0);
492 aBackTrans.translate(1.0, 0.0, 0.0);
493 aBackTrans.scale(fForwLen, getRadius(), getRadius());
494 aBackTrans *= aRotVector;
495 aBackTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
497 // line end edge, build transformed primitiveVector3D
498 TransformPrimitive3D* pNewTransformedD = new TransformPrimitive3D(aBackTrans, getLineCapSegments(nSegments, aMaterial));
499 aResultVector.push_back(pNewTransformedD);
503 // prepare next loop step
504 aLast = aCurr;
505 aCurr = aNext;
508 else
510 // create hairline
511 PolygonHairlinePrimitive3D* pNew = new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor());
512 aResultVector.push_back(pNew);
516 // prepare return value
517 Primitive3DSequence aRetval(aResultVector.size());
519 for(sal_uInt32 a(0L); a < aResultVector.size(); a++)
521 aRetval[a] = Primitive3DReference(aResultVector[a]);
524 return aRetval;
527 PolygonTubePrimitive3D::PolygonTubePrimitive3D(
528 const basegfx::B3DPolygon& rPolygon,
529 const basegfx::BColor& rBColor,
530 double fRadius, basegfx::B2DLineJoin aLineJoin,
531 double fDegreeStepWidth,
532 double fMiterMinimumAngle)
533 : PolygonHairlinePrimitive3D(rPolygon, rBColor),
534 maLast3DDecomposition(),
535 mfRadius(fRadius),
536 mfDegreeStepWidth(fDegreeStepWidth),
537 mfMiterMinimumAngle(fMiterMinimumAngle),
538 maLineJoin(aLineJoin)
542 bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
544 if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
546 const PolygonTubePrimitive3D& rCompare = (PolygonTubePrimitive3D&)rPrimitive;
548 return (getRadius() == rCompare.getRadius()
549 && getDegreeStepWidth() == rCompare.getDegreeStepWidth()
550 && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle()
551 && getLineJoin() == rCompare.getLineJoin());
554 return false;
557 Primitive3DSequence PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
559 ::osl::MutexGuard aGuard( m_aMutex );
561 if(!getLast3DDecomposition().hasElements())
563 const Primitive3DSequence aNewSequence(impCreate3DDecomposition(rViewInformation));
564 const_cast< PolygonTubePrimitive3D* >(this)->setLast3DDecomposition(aNewSequence);
567 return getLast3DDecomposition();
570 // provide unique ID
571 ImplPrimitrive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D)
573 } // end of namespace primitive3d
574 } // end of namespace drawinglayer
576 //////////////////////////////////////////////////////////////////////////////
577 // eof
579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */