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
44 namespace // anonymous namespace
46 Primitive3DSequence
getLineTubeSegments(
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
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
;
161 Primitive3DSequence
getLineJoinSegments(
162 sal_uInt32 nSegments
,
163 const attribute::MaterialAttribute3D
& rMaterial
,
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
;
175 if(basegfx::B2DLINEJOIN_ROUND
== aLineJoin
)
177 // calculate new horizontal segments
178 const sal_uInt32
nHorSeg((sal_uInt32
)((fAngle
/ F_2PI
) * (double)nSegments
));
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
);
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);
222 basegfx::B3DPoint aPointOnXY
, aPointRotY
, aNextPointOnXY
, aNextPointRotY
;
223 basegfx::B3DPoint aCurrMiter
, aNextMiter
;
224 basegfx::B3DPolygon aNewPolygon
, aMiterPolygon
;
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
);
239 aNextPointOnXY
= basegfx::B3DPoint(
244 aNextPointRotY
= basegfx::B3DPoint(
245 aNextPointOnXY
.getX() * fCos
,
246 aNextPointOnXY
.getY(),
247 aNextPointOnXY
.getX() * fSin
);
251 aNextMiter
= basegfx::B3DPoint(
252 aNextPointOnXY
.getX(),
253 aNextPointOnXY
.getY(),
254 fMiterSin
* (aNextPointOnXY
.getX() / fMiterCos
));
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
);
275 aNewPolygon
.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
276 aNewPolygon
.append(aNextPointOnXY
);
277 aNewPolygon
.append(aNextPointRotY
);
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
);
297 aNewPolygon
.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
298 aNewPolygon
.append(aPointRotY
);
299 aNewPolygon
.append(aPointOnXY
);
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
);
321 aNewPolygon
.append(aPointRotY
);
322 aNewPolygon
.append(aPointOnXY
);
323 aNewPolygon
.append(aNextPointOnXY
);
324 aNewPolygon
.append(aNextPointRotY
);
329 for(sal_uInt32
b(0L); b
< aNewPolygon
.count(); b
++)
331 aNewPolygon
.setNormal(b
, basegfx::B3DVector(aNewPolygon
.getB3DPoint(b
)));
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())
345 for(sal_uInt32
c(0L); c
< aMiterPolygon
.count(); c
++)
347 aMiterPolygon
.setNormal(c
, basegfx::B3DVector(aMiterPolygon
.getB3DPoint(c
)));
351 const basegfx::B3DPolyPolygon
aMiterPolyPolygon(aMiterPolygon
);
352 BasePrimitive3D
* pNew
= new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon
, rMaterial
, false);
353 aResultVector
.push_back(pNew
);
359 aPointOnXY
= aNextPointOnXY
;
360 aPointRotY
= aNextPointRotY
;
364 aCurrMiter
= aNextMiter
;
371 Primitive3DSequence
aRetval(aResultVector
.size());
373 for(sal_uInt32
a(0L); a
< aResultVector
.size(); a
++)
375 aRetval
[a
] = Primitive3DReference(aResultVector
[a
]);
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);
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
++)
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
);
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
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
]);
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(),
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());
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();
571 ImplPrimitrive3DIDBlock(PolygonTubePrimitive3D
, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D
)
573 } // end of namespace primitive3d
574 } // end of namespace drawinglayer
576 //////////////////////////////////////////////////////////////////////////////
579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */