1 /*************************************************************************
3 * OpenOffice.org - a multi-platform office productivity suite
5 * $RCSfile: polygontubeprimitive3d.cxx,v $
9 * last change: $Author: aw $ $Date: 2008-06-10 09:29:33 $
11 * The Contents of this file are made available subject to
12 * the terms of GNU Lesser General Public License Version 2.1.
15 * GNU Lesser General Public License Version 2.1
16 * =============================================
17 * Copyright 2005 by Sun Microsystems, Inc.
18 * 901 San Antonio Road, Palo Alto, CA 94303, USA
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License version 2.1, as published by the Free Software Foundation.
24 * This library is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * Lesser General Public License for more details.
29 * You should have received a copy of the GNU Lesser General Public
30 * License along with this library; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
34 ************************************************************************/
36 // MARKER(update_precomp.py): autogen include statement, do not remove
37 #include "precompiled_drawinglayer.hxx"
39 #include <drawinglayer/primitive3d/polygontubeprimitive3d.hxx>
40 #include <drawinglayer/attribute/materialattribute3d.hxx>
41 #include <basegfx/matrix/b3dhommatrix.hxx>
42 #include <basegfx/polygon/b3dpolypolygon.hxx>
43 #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
44 #include <basegfx/polygon/b3dpolypolygontools.hxx>
45 #include <drawinglayer/primitive3d/transformprimitive3d.hxx>
46 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
48 //////////////////////////////////////////////////////////////////////////////
50 namespace drawinglayer
54 namespace // anonymous namespace
56 Primitive3DSequence
getLineTubeSegments(
58 const attribute::MaterialAttribute3D
& rMaterial
)
60 // static data for buffered tube primitives
61 static Primitive3DSequence aLineTubeList
;
62 static sal_uInt32
nLineTubeSegments(0L);
63 static attribute::MaterialAttribute3D aLineMaterial
;
65 // may exclusively change static data, use mutex
68 if(nSegments
!= nLineTubeSegments
|| rMaterial
!= aLineMaterial
)
70 nLineTubeSegments
= nSegments
;
71 aLineMaterial
= rMaterial
;
72 aLineTubeList
= Primitive3DSequence();
75 if(!aLineTubeList
.hasElements() && 0L != nLineTubeSegments
)
77 const basegfx::B3DPoint
aLeft(0.0, 0.0, 0.0);
78 const basegfx::B3DPoint
aRight(1.0, 0.0, 0.0);
79 basegfx::B3DPoint
aLastLeft(0.0, 1.0, 0.0);
80 basegfx::B3DPoint
aLastRight(1.0, 1.0, 0.0);
81 basegfx::B3DHomMatrix aRot
;
82 aRot
.rotate(F_2PI
/ (double)nLineTubeSegments
, 0.0, 0.0);
83 aLineTubeList
.realloc(nLineTubeSegments
);
85 for(sal_uInt32
a(0L); a
< nLineTubeSegments
; a
++)
87 const basegfx::B3DPoint
aNextLeft(aRot
* aLastLeft
);
88 const basegfx::B3DPoint
aNextRight(aRot
* aLastRight
);
89 basegfx::B3DPolygon aNewPolygon
;
91 aNewPolygon
.append(aNextLeft
);
92 aNewPolygon
.setNormal(0L, basegfx::B3DVector(aNextLeft
- aLeft
));
94 aNewPolygon
.append(aLastLeft
);
95 aNewPolygon
.setNormal(1L, basegfx::B3DVector(aLastLeft
- aLeft
));
97 aNewPolygon
.append(aLastRight
);
98 aNewPolygon
.setNormal(2L, basegfx::B3DVector(aLastRight
- aRight
));
100 aNewPolygon
.append(aNextRight
);
101 aNewPolygon
.setNormal(3L, basegfx::B3DVector(aNextRight
- aRight
));
103 aNewPolygon
.setClosed(true);
105 const basegfx::B3DPolyPolygon
aNewPolyPolygon(aNewPolygon
);
106 const Primitive3DReference
xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon
, aLineMaterial
, false));
107 aLineTubeList
[a
] = xRef
;
109 aLastLeft
= aNextLeft
;
110 aLastRight
= aNextRight
;
114 return aLineTubeList
;
117 Primitive3DSequence
getLineCapSegments(
118 sal_uInt32 nSegments
,
119 const attribute::MaterialAttribute3D
& rMaterial
)
121 // static data for buffered tube primitives
122 static Primitive3DSequence aLineCapList
;
123 static sal_uInt32
nLineCapSegments(0L);
124 static attribute::MaterialAttribute3D aLineMaterial
;
126 // may exclusively change static data, use mutex
127 ::osl::Mutex m_mutex
;
129 if(nSegments
!= nLineCapSegments
|| rMaterial
!= aLineMaterial
)
131 nLineCapSegments
= nSegments
;
132 aLineMaterial
= rMaterial
;
133 aLineCapList
= Primitive3DSequence();
136 if(!aLineCapList
.hasElements() && 0L != nLineCapSegments
)
138 const basegfx::B3DPoint
aNull(0.0, 0.0, 0.0);
139 basegfx::B3DPoint
aLast(0.0, 1.0, 0.0);
140 basegfx::B3DHomMatrix aRot
;
141 aRot
.rotate(F_2PI
/ (double)nLineCapSegments
, 0.0, 0.0);
142 aLineCapList
.realloc(nLineCapSegments
);
144 for(sal_uInt32
a(0L); a
< nLineCapSegments
; a
++)
146 const basegfx::B3DPoint
aNext(aRot
* aLast
);
147 basegfx::B3DPolygon aNewPolygon
;
149 aNewPolygon
.append(aLast
);
150 aNewPolygon
.setNormal(0L, basegfx::B3DVector(aLast
- aNull
));
152 aNewPolygon
.append(aNext
);
153 aNewPolygon
.setNormal(1L, basegfx::B3DVector(aNext
- aNull
));
155 aNewPolygon
.append(aNull
);
156 aNewPolygon
.setNormal(2L, basegfx::B3DVector(-1.0, 0.0, 0.0));
158 aNewPolygon
.setClosed(true);
160 const basegfx::B3DPolyPolygon
aNewPolyPolygon(aNewPolygon
);
161 const Primitive3DReference
xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon
, aLineMaterial
, false));
162 aLineCapList
[a
] = xRef
;
171 Primitive3DSequence
getLineJoinSegments(
172 sal_uInt32 nSegments
,
173 const attribute::MaterialAttribute3D
& rMaterial
,
175 double /*fDegreeStepWidth*/,
176 double fMiterMinimumAngle
,
177 basegfx::B2DLineJoin aLineJoin
)
179 // nSegments is for whole circle, adapt to half circle
180 const sal_uInt32
nVerSeg(nSegments
>> 1L);
181 std::vector
< BasePrimitive3D
* > aResultVector
;
185 if(basegfx::B2DLINEJOIN_ROUND
== aLineJoin
)
187 // calculate new horizontal segments
188 const sal_uInt32
nHorSeg((sal_uInt32
)((fAngle
/ F_2PI
) * (double)nSegments
));
192 // create half-sphere
193 const basegfx::B3DPolyPolygon
aSphere(basegfx::tools::createUnitSphereFillPolyPolygon(nHorSeg
, nVerSeg
, true, F_PI2
, -F_PI2
, 0.0, fAngle
));
195 for(sal_uInt32
a(0L); a
< aSphere
.count(); a
++)
197 const basegfx::B3DPolygon
aPartPolygon(aSphere
.getB3DPolygon(a
));
198 const basegfx::B3DPolyPolygon
aPartPolyPolygon(aPartPolygon
);
199 BasePrimitive3D
* pNew
= new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon
, rMaterial
, false);
200 aResultVector
.push_back(pNew
);
205 // fallback to bevel when there is not at least one segment hor and ver
206 aLineJoin
= basegfx::B2DLINEJOIN_BEVEL
;
210 if(basegfx::B2DLINEJOIN_MIDDLE
== aLineJoin
211 || basegfx::B2DLINEJOIN_BEVEL
== aLineJoin
212 || basegfx::B2DLINEJOIN_MITER
== aLineJoin
)
214 if(basegfx::B2DLINEJOIN_MITER
== aLineJoin
)
216 const double fMiterAngle(fAngle
/2.0);
218 if(fMiterAngle
< fMiterMinimumAngle
)
220 // fallback to bevel when miter's angle is too small
221 aLineJoin
= basegfx::B2DLINEJOIN_BEVEL
;
225 const double fInc(F_PI
/ (double)nVerSeg
);
226 const double fSin(sin(-fAngle
));
227 const double fCos(cos(-fAngle
));
228 const bool bMiter(basegfx::B2DLINEJOIN_MITER
== aLineJoin
);
229 const double fMiterSin(bMiter
? sin(-(fAngle
/2.0)) : 0.0);
230 const double fMiterCos(bMiter
? cos(-(fAngle
/2.0)) : 0.0);
232 basegfx::B3DPoint aPointOnXY
, aPointRotY
, aNextPointOnXY
, aNextPointRotY
;
233 basegfx::B3DPoint aCurrMiter
, aNextMiter
;
234 basegfx::B3DPolygon aNewPolygon
, aMiterPolygon
;
237 aNewPolygon
.setClosed(true);
238 aMiterPolygon
.setClosed(true);
240 for(sal_uInt32
a(0L); a
< nVerSeg
; a
++)
242 const bool bFirst(0L == a
);
243 const bool bLast(a
+ 1L == nVerSeg
);
249 aNextPointOnXY
= basegfx::B3DPoint(
254 aNextPointRotY
= basegfx::B3DPoint(
255 aNextPointOnXY
.getX() * fCos
,
256 aNextPointOnXY
.getY(),
257 aNextPointOnXY
.getX() * fSin
);
261 aNextMiter
= basegfx::B3DPoint(
262 aNextPointOnXY
.getX(),
263 aNextPointOnXY
.getY(),
264 fMiterSin
* (aNextPointOnXY
.getX() / fMiterCos
));
274 aNewPolygon
.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
275 aNewPolygon
.append(aNextPointOnXY
);
276 aNewPolygon
.append(aNextMiter
);
278 aMiterPolygon
.clear();
279 aMiterPolygon
.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
280 aMiterPolygon
.append(aNextMiter
);
281 aMiterPolygon
.append(aNextPointRotY
);
285 aNewPolygon
.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
286 aNewPolygon
.append(aNextPointOnXY
);
287 aNewPolygon
.append(aNextPointRotY
);
296 aNewPolygon
.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
297 aNewPolygon
.append(aCurrMiter
);
298 aNewPolygon
.append(aPointOnXY
);
300 aMiterPolygon
.clear();
301 aMiterPolygon
.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
302 aMiterPolygon
.append(aPointRotY
);
303 aMiterPolygon
.append(aCurrMiter
);
307 aNewPolygon
.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
308 aNewPolygon
.append(aPointRotY
);
309 aNewPolygon
.append(aPointOnXY
);
318 aNewPolygon
.append(aPointOnXY
);
319 aNewPolygon
.append(aNextPointOnXY
);
320 aNewPolygon
.append(aNextMiter
);
321 aNewPolygon
.append(aCurrMiter
);
323 aMiterPolygon
.clear();
324 aMiterPolygon
.append(aCurrMiter
);
325 aMiterPolygon
.append(aNextMiter
);
326 aMiterPolygon
.append(aNextPointRotY
);
327 aMiterPolygon
.append(aPointRotY
);
331 aNewPolygon
.append(aPointRotY
);
332 aNewPolygon
.append(aPointOnXY
);
333 aNewPolygon
.append(aNextPointOnXY
);
334 aNewPolygon
.append(aNextPointRotY
);
339 for(sal_uInt32
b(0L); b
< aNewPolygon
.count(); b
++)
341 aNewPolygon
.setNormal(b
, basegfx::B3DVector(aNewPolygon
.getB3DPoint(b
)));
345 if(aNewPolygon
.count())
347 const basegfx::B3DPolyPolygon
aNewPolyPolygon(aNewPolygon
);
348 BasePrimitive3D
* pNew
= new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon
, rMaterial
, false);
349 aResultVector
.push_back(pNew
);
352 if(bMiter
&& aMiterPolygon
.count())
355 for(sal_uInt32
c(0L); c
< aMiterPolygon
.count(); c
++)
357 aMiterPolygon
.setNormal(c
, basegfx::B3DVector(aMiterPolygon
.getB3DPoint(c
)));
361 const basegfx::B3DPolyPolygon
aMiterPolyPolygon(aMiterPolygon
);
362 BasePrimitive3D
* pNew
= new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon
, rMaterial
, false);
363 aResultVector
.push_back(pNew
);
369 aPointOnXY
= aNextPointOnXY
;
370 aPointRotY
= aNextPointRotY
;
374 aCurrMiter
= aNextMiter
;
381 Primitive3DSequence
aRetval(aResultVector
.size());
383 for(sal_uInt32
a(0L); a
< aResultVector
.size(); a
++)
385 aRetval
[a
] = Primitive3DReference(aResultVector
[a
]);
391 basegfx::B3DHomMatrix
getRotationFromVector(const basegfx::B3DVector
& rVector
)
393 // build transformation from unit vector to vector
394 basegfx::B3DHomMatrix aRetval
;
396 // get applied rotations from angles in XY and in XZ (cartesian)
397 const double fRotInXY(atan2(rVector
.getY(), rVector
.getXZLength()));
398 const double fRotInXZ(atan2(-rVector
.getZ(), rVector
.getX()));
400 // apply rotations. Rot around Z needs to be done first, so apply in two steps
401 aRetval
.rotate(0.0, 0.0, fRotInXY
);
402 aRetval
.rotate(0.0, fRotInXZ
, 0.0);
406 } // end of anonymous namespace
407 } // end of namespace primitive3d
408 } // end of namespace drawinglayer
410 //////////////////////////////////////////////////////////////////////////////
412 using namespace com::sun::star
;
414 //////////////////////////////////////////////////////////////////////////////
416 namespace drawinglayer
418 namespace primitive3d
420 Primitive3DSequence
PolygonTubePrimitive3D::createLocalDecomposition(const geometry::ViewInformation3D
& /*rViewInformation*/) const
422 const sal_uInt32
nPointCount(getB3DPolygon().count());
423 std::vector
< BasePrimitive3D
* > aResultVector
;
425 if(0L != nPointCount
)
427 if(basegfx::fTools::more(getRadius(), 0.0))
429 const attribute::MaterialAttribute3D
aMaterial(getBColor());
430 static sal_uInt32
nSegments(8L); // default for 3d line segments, for more quality just raise this value (in even steps)
431 const bool bClosed(getB3DPolygon().isClosed());
432 const bool bNoLineJoin(basegfx::B2DLINEJOIN_NONE
== getLineJoin());
433 const sal_uInt32
nLoopCount(bClosed
? nPointCount
: nPointCount
- 1L);
434 basegfx::B3DPoint
aLast(getB3DPolygon().getB3DPoint(nPointCount
- 1L));
435 basegfx::B3DPoint
aCurr(getB3DPolygon().getB3DPoint(0L));
437 for(sal_uInt32
a(0L); a
< nLoopCount
; a
++)
440 const basegfx::B3DPoint
aNext(getB3DPolygon().getB3DPoint((a
+ 1L) % nPointCount
));
441 const basegfx::B3DVector
aForw(aNext
- aCurr
);
442 const double fForwLen(aForw
.getLength());
444 if(basegfx::fTools::more(fForwLen
, 0.0))
446 // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
447 basegfx::B3DHomMatrix
aRotVector(getRotationFromVector(aForw
));
449 // create default transformation with scale and rotate
450 basegfx::B3DHomMatrix aVectorTrans
;
451 aVectorTrans
.scale(fForwLen
, getRadius(), getRadius());
452 aVectorTrans
*= aRotVector
;
453 aVectorTrans
.translate(aCurr
.getX(), aCurr
.getY(), aCurr
.getZ());
455 if(bNoLineJoin
|| (!bClosed
&& !a
))
457 // line start edge, build transformed primitiveVector3D
458 TransformPrimitive3D
* pNewTransformedA
= new TransformPrimitive3D(aVectorTrans
, getLineCapSegments(nSegments
, aMaterial
));
459 aResultVector
.push_back(pNewTransformedA
);
463 const basegfx::B3DVector
aBack(aCurr
- aLast
);
464 const double fCross(basegfx::cross(aBack
, aForw
).getLength());
466 if(!basegfx::fTools::equalZero(fCross
))
468 // line connect non-parallel, aBack, aForw, use getLineJoin()
469 const double fAngle(acos(aBack
.scalar(aForw
) / (fForwLen
* aBack
.getLength()))); // 0.0 .. F_PI2
470 Primitive3DSequence
aNewList(getLineJoinSegments(nSegments
, aMaterial
, fAngle
, getDegreeStepWidth(), getMiterMinimumAngle(), getLineJoin()));
472 // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
473 basegfx::B3DHomMatrix
aInvRotVector(aRotVector
);
474 aInvRotVector
.invert();
475 basegfx::B3DVector
aTransBack(aInvRotVector
* aBack
);
476 const double fRotInYZ(atan2(aTransBack
.getY(), aTransBack
.getZ()));
478 // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
479 // Also apply usual scaling and translation
480 basegfx::B3DHomMatrix aSphereTrans
;
481 aSphereTrans
.rotate(0.0, F_PI2
, 0.0);
482 aSphereTrans
.rotate(F_PI
- fRotInYZ
, 0.0, 0.0);
483 aSphereTrans
*= aRotVector
;
484 aSphereTrans
.scale(getRadius(), getRadius(), getRadius());
485 aSphereTrans
.translate(aCurr
.getX(), aCurr
.getY(), aCurr
.getZ());
487 // line start edge, build transformed primitiveVector3D
488 TransformPrimitive3D
* pNewTransformedB
= new TransformPrimitive3D(aSphereTrans
, aNewList
);
489 aResultVector
.push_back(pNewTransformedB
);
493 // create line segments, build transformed primitiveVector3D
494 TransformPrimitive3D
* pNewTransformedC
= new TransformPrimitive3D(aVectorTrans
, getLineTubeSegments(nSegments
, aMaterial
));
495 aResultVector
.push_back(pNewTransformedC
);
497 if(bNoLineJoin
|| (!bClosed
&& ((a
+ 1L) == nLoopCount
)))
499 // line end edge, first rotate (mirror) and translate, then use use aRotVector
500 basegfx::B3DHomMatrix aBackTrans
;
501 aBackTrans
.rotate(0.0, F_PI
, 0.0);
502 aBackTrans
.translate(1.0, 0.0, 0.0);
503 aBackTrans
.scale(fForwLen
, getRadius(), getRadius());
504 aBackTrans
*= aRotVector
;
505 aBackTrans
.translate(aCurr
.getX(), aCurr
.getY(), aCurr
.getZ());
507 // line end edge, build transformed primitiveVector3D
508 TransformPrimitive3D
* pNewTransformedD
= new TransformPrimitive3D(aBackTrans
, getLineCapSegments(nSegments
, aMaterial
));
509 aResultVector
.push_back(pNewTransformedD
);
513 // prepare next loop step
521 PolygonHairlinePrimitive3D
* pNew
= new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor());
522 aResultVector
.push_back(pNew
);
526 // prepare return value
527 Primitive3DSequence
aRetval(aResultVector
.size());
529 for(sal_uInt32
a(0L); a
< aResultVector
.size(); a
++)
531 aRetval
[a
] = Primitive3DReference(aResultVector
[a
]);
537 PolygonTubePrimitive3D::PolygonTubePrimitive3D(
538 const basegfx::B3DPolygon
& rPolygon
,
539 const basegfx::BColor
& rBColor
,
540 double fRadius
, basegfx::B2DLineJoin aLineJoin
,
541 double fDegreeStepWidth
,
542 double fMiterMinimumAngle
)
543 : PolygonHairlinePrimitive3D(rPolygon
, rBColor
),
545 mfDegreeStepWidth(fDegreeStepWidth
),
546 mfMiterMinimumAngle(fMiterMinimumAngle
),
547 maLineJoin(aLineJoin
)
551 bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D
& rPrimitive
) const
553 if(PolygonHairlinePrimitive3D::operator==(rPrimitive
))
555 const PolygonTubePrimitive3D
& rCompare
= (PolygonTubePrimitive3D
&)rPrimitive
;
557 return (getRadius() == rCompare
.getRadius()
558 && getDegreeStepWidth() == rCompare
.getDegreeStepWidth()
559 && getMiterMinimumAngle() == rCompare
.getMiterMinimumAngle()
560 && getLineJoin() == rCompare
.getLineJoin());
567 ImplPrimitrive3DIDBlock(PolygonTubePrimitive3D
, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D
)
569 } // end of namespace primitive3d
570 } // end of namespace drawinglayer
572 //////////////////////////////////////////////////////////////////////////////