1 /*************************************************************************
3 * OpenOffice.org - a multi-platform office productivity suite
5 * $RCSfile: sdrextrudeprimitive3d.cxx,v $
9 * last change: $Author: aw $ $Date: 2008-06-26 16:21:48 $
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/sdrextrudeprimitive3d.hxx>
40 #include <basegfx/matrix/b2dhommatrix.hxx>
41 #include <basegfx/polygon/b2dpolygontools.hxx>
42 #include <basegfx/polygon/b3dpolypolygontools.hxx>
43 #include <drawinglayer/primitive3d/sdrdecompositiontools3d.hxx>
44 #include <basegfx/tools/canvastools.hxx>
45 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
46 #include <drawinglayer/geometry/viewinformation3d.hxx>
47 #include <drawinglayer/primitive3d/hittestprimitive3d.hxx>
48 #include <drawinglayer/attribute/sdrattribute.hxx>
50 //////////////////////////////////////////////////////////////////////////////
52 using namespace com::sun::star
;
54 //////////////////////////////////////////////////////////////////////////////
56 namespace drawinglayer
60 Primitive3DSequence
SdrExtrudePrimitive3D::createLocalDecomposition(const geometry::ViewInformation3D
& rViewInformation
) const
62 Primitive3DSequence aRetval
;
65 const Slice3DVector
& rSliceVector
= getSlices();
67 if(rSliceVector
.size())
71 // decide what to create
72 const ::com::sun::star::drawing::NormalsKind
eNormalsKind(getSdr3DObjectAttribute().getNormalsKind());
73 const bool bCreateNormals(::com::sun::star::drawing::NormalsKind_SPECIFIC
== eNormalsKind
);
74 const bool bCreateTextureCoordiantesX(::com::sun::star::drawing::TextureProjectionMode_OBJECTSPECIFIC
== getSdr3DObjectAttribute().getTextureProjectionX());
75 const bool bCreateTextureCoordiantesY(::com::sun::star::drawing::TextureProjectionMode_OBJECTSPECIFIC
== getSdr3DObjectAttribute().getTextureProjectionY());
76 double fRelativeTextureWidth(1.0);
77 basegfx::B2DHomMatrix aTexTransform
;
79 if(getSdrLFSAttribute().getFill() && (bCreateTextureCoordiantesX
|| bCreateTextureCoordiantesY
))
81 const basegfx::B2DPolygon
aFirstPolygon(maCorrectedPolyPolygon
.getB2DPolygon(0L));
82 const double fFrontLength(basegfx::tools::getLength(aFirstPolygon
));
83 const double fFrontArea(basegfx::tools::getArea(aFirstPolygon
));
84 const double fSqrtFrontArea(sqrt(fFrontArea
));
85 fRelativeTextureWidth
= basegfx::fTools::equalZero(fSqrtFrontArea
) ? 1.0 : fFrontLength
/ fSqrtFrontArea
;
86 fRelativeTextureWidth
= (double)((sal_uInt32
)(fRelativeTextureWidth
- 0.5));
88 if(fRelativeTextureWidth
< 1.0)
90 fRelativeTextureWidth
= 1.0;
93 aTexTransform
.translate(-0.5, -0.5);
94 aTexTransform
.scale(-1.0, -1.0);
95 aTexTransform
.translate(0.5, 0.5);
96 aTexTransform
.scale(fRelativeTextureWidth
, 1.0);
100 ::std::vector
< basegfx::B3DPolyPolygon
> aFill
;
101 extractPlanesFromSlice(aFill
, rSliceVector
,
102 bCreateNormals
, getSmoothHorizontalNormals(), getSmoothNormals(), getSmoothLids(), false,
103 0.5, 0.6, bCreateTextureCoordiantesX
|| bCreateTextureCoordiantesY
, aTexTransform
);
106 const basegfx::B3DRange
aRange(getRangeFrom3DGeometry(aFill
));
109 if(getSdrLFSAttribute().getFill())
111 if(::com::sun::star::drawing::NormalsKind_SPHERE
== eNormalsKind
)
113 applyNormalsKindSphereTo3DGeometry(aFill
, aRange
);
115 else if(::com::sun::star::drawing::NormalsKind_FLAT
== eNormalsKind
)
117 applyNormalsKindFlatTo3DGeometry(aFill
);
120 if(getSdr3DObjectAttribute().getNormalsInvert())
122 applyNormalsInvertTo3DGeometry(aFill
);
126 // texture coordinates
127 if(getSdrLFSAttribute().getFill())
129 applyTextureTo3DGeometry(
130 getSdr3DObjectAttribute().getTextureProjectionX(),
131 getSdr3DObjectAttribute().getTextureProjectionY(),
137 if(getSdrLFSAttribute().getFill())
140 aRetval
= create3DPolyPolygonFillPrimitives(
144 getSdr3DObjectAttribute(),
145 *getSdrLFSAttribute().getFill(),
146 getSdrLFSAttribute().getFillFloatTransGradient());
150 // create simplified 3d hit test geometry
151 const attribute::SdrFillAttribute
aSimplifiedFillAttribute(0.0, basegfx::BColor(), 0, 0, 0);
153 aRetval
= create3DPolyPolygonFillPrimitives(
157 getSdr3DObjectAttribute(),
158 aSimplifiedFillAttribute
,
161 // encapsulate in HitTestPrimitive3D and add
162 const Primitive3DReference
xRef(new HitTestPrimitive3D(aRetval
));
163 aRetval
= Primitive3DSequence(&xRef
, 1L);
167 if(getSdrLFSAttribute().getLine())
169 if(getSdr3DObjectAttribute().getReducedLineGeometry())
171 // create geometric outlines with reduced line geometry for chart.
172 const basegfx::B3DPolyPolygon
aVerLine(extractVerticalLinesFromSlice(rSliceVector
));
173 const sal_uInt32
nCount(aVerLine
.count());
174 basegfx::B3DPolyPolygon aReducedLoops
;
175 basegfx::B3DPolyPolygon aNewLineGeometry
;
177 // sort out doubles (front and back planes when no edge rounding is done). Since
178 // this is a line geometry merged from PolyPolygons, loop over all Polygons
179 for(a
= 0; a
< nCount
; a
++)
181 const sal_uInt32
nReducedCount(aReducedLoops
.count());
182 const basegfx::B3DPolygon
aCandidate(aVerLine
.getB3DPolygon(a
));
187 for(sal_uInt32
b(0); bAdd
&& b
< nReducedCount
; b
++)
189 if(aCandidate
== aReducedLoops
.getB3DPolygon(b
))
198 aReducedLoops
.append(aCandidate
);
202 // from here work with reduced loops and reduced count without changing them
203 const sal_uInt32
nReducedCount(aReducedLoops
.count());
205 if(nReducedCount
> 1)
207 for(sal_uInt32
b(1); b
< nReducedCount
; b
++)
210 const basegfx::B3DPolygon
aCandA(aReducedLoops
.getB3DPolygon(b
- 1));
211 const basegfx::B3DPolygon
aCandB(aReducedLoops
.getB3DPolygon(b
));
213 // for each loop pair create the connection edges
214 createReducedOutlines(
223 // add reduced loops themselves
224 aNewLineGeometry
.append(aReducedLoops
);
226 // to create vertical edges at non-C1/C2 steady loops, use maCorrectedPolyPolygon
227 // directly since the 3D Polygons do not suport this.
229 // Unfortunately there is no bezier polygon provided by the chart module; one reason is
230 // that the API for extrude wants a 3D polygon geometry (for historical reasons, i guess)
231 // and those have no beziers. Another reason is that he chart module uses self-created
232 // stuff to create the 2D geometry (in ShapeFactory::createPieSegment), but this geometry
233 // does not contain bezier infos, either. The only way which is possible for now is to 'detect'
234 // candidates for vertical edges of pie segments by looking for the angles in the polygon.
236 // This is all not very well designed ATM. Ideally, the ReducedLineGeometry is responsible
237 // for creating the outer geometry edges (createReducedOutlines), but for special edges
238 // like the vertical ones for pie center and both start/end, the incarnation with the
239 // knowledge about that it needs to create those and IS a pie segment -> in this case,
241 const sal_uInt32
nPolyCount(maCorrectedPolyPolygon
.count());
243 for(sal_uInt32
c(0); c
< nPolyCount
; c
++)
245 const basegfx::B2DPolygon
aCandidate(maCorrectedPolyPolygon
.getB2DPolygon(c
));
246 const sal_uInt32
nPointCount(aCandidate
.count());
250 sal_uInt32
nIndexA(nPointCount
);
251 sal_uInt32
nIndexB(nPointCount
);
252 sal_uInt32
nIndexC(nPointCount
);
254 for(sal_uInt32
d(0); d
< nPointCount
; d
++)
256 const sal_uInt32
nPrevInd((d
+ nPointCount
- 1) % nPointCount
);
257 const sal_uInt32
nNextInd((d
+ 1) % nPointCount
);
258 const basegfx::B2DPoint
aPoint(aCandidate
.getB2DPoint(d
));
259 const basegfx::B2DVector
aPrev(aCandidate
.getB2DPoint(nPrevInd
) - aPoint
);
260 const basegfx::B2DVector
aNext(aCandidate
.getB2DPoint(nNextInd
) - aPoint
);
261 const double fAngle(aPrev
.angle(aNext
));
263 // take each angle which deviates more than 10% from going straight as
264 // special edge. This will detect the two outer edges of pie segments,
265 // but not always the center one (think about a near 180 degree pie)
266 if(F_PI
- fabs(fAngle
) > F_PI
* 0.1)
268 if(nPointCount
== nIndexA
)
272 else if(nPointCount
== nIndexB
)
276 else if(nPointCount
== nIndexC
)
284 const bool bIndexAUsed(nIndexA
!= nPointCount
);
285 const bool bIndexBUsed(nIndexB
!= nPointCount
);
286 bool bIndexCUsed(nIndexC
!= nPointCount
);
290 // already three special edges found, so the center one was already detected
291 // and does not need to be searched
293 else if(bIndexAUsed
&& bIndexBUsed
)
295 // outer edges detected (they are approx. 90 degrees), but center one not.
296 // Look with the knowledge that it's in-between the two found ones
297 if(((nIndexA
+ 2) % nPointCount
) == nIndexB
)
299 nIndexC
= (nIndexA
+ 1) % nPointCount
;
301 else if(((nIndexA
+ nPointCount
- 2) % nPointCount
) == nIndexB
)
303 nIndexC
= (nIndexA
+ nPointCount
- 1) % nPointCount
;
306 bIndexCUsed
= (nIndexC
!= nPointCount
);
311 const basegfx::B2DPoint
aPoint(aCandidate
.getB2DPoint(nIndexA
));
312 const basegfx::B3DPoint
aStart(aPoint
.getX(), aPoint
.getY(), 0.0);
313 const basegfx::B3DPoint
aEnd(aPoint
.getX(), aPoint
.getY(), getDepth());
314 basegfx::B3DPolygon aToBeAdded
;
316 aToBeAdded
.append(aStart
);
317 aToBeAdded
.append(aEnd
);
318 aNewLineGeometry
.append(aToBeAdded
);
323 const basegfx::B2DPoint
aPoint(aCandidate
.getB2DPoint(nIndexB
));
324 const basegfx::B3DPoint
aStart(aPoint
.getX(), aPoint
.getY(), 0.0);
325 const basegfx::B3DPoint
aEnd(aPoint
.getX(), aPoint
.getY(), getDepth());
326 basegfx::B3DPolygon aToBeAdded
;
328 aToBeAdded
.append(aStart
);
329 aToBeAdded
.append(aEnd
);
330 aNewLineGeometry
.append(aToBeAdded
);
335 const basegfx::B2DPoint
aPoint(aCandidate
.getB2DPoint(nIndexC
));
336 const basegfx::B3DPoint
aStart(aPoint
.getX(), aPoint
.getY(), 0.0);
337 const basegfx::B3DPoint
aEnd(aPoint
.getX(), aPoint
.getY(), getDepth());
338 basegfx::B3DPolygon aToBeAdded
;
340 aToBeAdded
.append(aStart
);
341 aToBeAdded
.append(aEnd
);
342 aNewLineGeometry
.append(aToBeAdded
);
347 // append loops themselves
348 aNewLineGeometry
.append(aReducedLoops
);
350 if(aNewLineGeometry
.count())
352 const Primitive3DSequence
aLines(create3DPolyPolygonLinePrimitives(aNewLineGeometry
, getTransform(), *getSdrLFSAttribute().getLine()));
353 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval
, aLines
);
358 // extract line geometry from slices
359 const basegfx::B3DPolyPolygon
aHorLine(extractHorizontalLinesFromSlice(rSliceVector
, false));
360 const basegfx::B3DPolyPolygon
aVerLine(extractVerticalLinesFromSlice(rSliceVector
));
362 // add horizontal lines
363 const Primitive3DSequence
aHorLines(create3DPolyPolygonLinePrimitives(aHorLine
, getTransform(), *getSdrLFSAttribute().getLine()));
364 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval
, aHorLines
);
366 // add vertical lines
367 const Primitive3DSequence
aVerLines(create3DPolyPolygonLinePrimitives(aVerLine
, getTransform(), *getSdrLFSAttribute().getLine()));
368 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval
, aVerLines
);
373 if(getSdrLFSAttribute().getShadow() && aRetval
.hasElements())
375 const Primitive3DSequence
aShadow(createShadowPrimitive3D(aRetval
, *getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
376 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval
, aShadow
);
383 void SdrExtrudePrimitive3D::impCreateSlices()
385 // prepare the polygon. No double points, correct orientations and a correct
386 // outmost polygon are needed
387 maCorrectedPolyPolygon
= getPolyPolygon();
388 maCorrectedPolyPolygon
.removeDoublePoints();
389 maCorrectedPolyPolygon
= basegfx::tools::correctOrientations(maCorrectedPolyPolygon
);
390 maCorrectedPolyPolygon
= basegfx::tools::correctOutmostPolygon(maCorrectedPolyPolygon
);
392 // prepare slices as geometry
393 createExtrudeSlices(maSlices
, maCorrectedPolyPolygon
, getBackScale(), getDiagonal(), getDepth(), getCharacterMode(), getCloseFront(), getCloseBack());
396 const Slice3DVector
& SdrExtrudePrimitive3D::getSlices() const
398 // This can be made dependent of getSdrLFSAttribute().getFill() and getSdrLFSAttribute().getLine()
399 // again when no longer geometry is needed for non-visible 3D objects as it is now for chart
400 if(getPolyPolygon().count() && !maSlices
.size())
402 ::osl::Mutex m_mutex
;
403 const_cast< SdrExtrudePrimitive3D
& >(*this).impCreateSlices();
409 SdrExtrudePrimitive3D::SdrExtrudePrimitive3D(
410 const basegfx::B3DHomMatrix
& rTransform
,
411 const basegfx::B2DVector
& rTextureSize
,
412 const attribute::SdrLineFillShadowAttribute
& rSdrLFSAttribute
,
413 const attribute::Sdr3DObjectAttribute
& rSdr3DObjectAttribute
,
414 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
419 bool bSmoothHorizontalNormals
,
424 : SdrPrimitive3D(rTransform
, rTextureSize
, rSdrLFSAttribute
, rSdr3DObjectAttribute
),
425 maCorrectedPolyPolygon(),
427 maPolyPolygon(rPolyPolygon
),
429 mfDiagonal(fDiagonal
),
430 mfBackScale(fBackScale
),
431 mpLastRLGViewInformation(0),
432 mbSmoothNormals(bSmoothNormals
),
433 mbSmoothHorizontalNormals(bSmoothHorizontalNormals
),
434 mbSmoothLids(bSmoothLids
),
435 mbCharacterMode(bCharacterMode
),
436 mbCloseFront(bCloseFront
),
437 mbCloseBack(bCloseBack
)
439 // make sure depth is positive
440 if(basegfx::fTools::lessOrEqual(getDepth(), 0.0))
445 // make sure the percentage value getDiagonal() is between 0.0 and 1.0
446 if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0))
450 else if(basegfx::fTools::moreOrEqual(getDiagonal(), 1.0))
455 // no close front/back when polygon is not closed
456 if(getPolyPolygon().count() && !getPolyPolygon().getB2DPolygon(0L).isClosed())
458 mbCloseFront
= mbCloseBack
= false;
461 // no edge rounding when not closing
462 if(!getCloseFront() && !getCloseBack())
468 SdrExtrudePrimitive3D::~SdrExtrudePrimitive3D()
470 if(mpLastRLGViewInformation
)
472 delete mpLastRLGViewInformation
;
476 bool SdrExtrudePrimitive3D::operator==(const BasePrimitive3D
& rPrimitive
) const
478 if(SdrPrimitive3D::operator==(rPrimitive
))
480 const SdrExtrudePrimitive3D
& rCompare
= static_cast< const SdrExtrudePrimitive3D
& >(rPrimitive
);
482 return (getPolyPolygon() == rCompare
.getPolyPolygon()
483 && getDepth() == rCompare
.getDepth()
484 && getDiagonal() == rCompare
.getDiagonal()
485 && getBackScale() == rCompare
.getBackScale()
486 && getSmoothNormals() == rCompare
.getSmoothNormals()
487 && getSmoothHorizontalNormals() == rCompare
.getSmoothHorizontalNormals()
488 && getSmoothLids() == rCompare
.getSmoothLids()
489 && getCharacterMode() == rCompare
.getCharacterMode()
490 && getCloseFront() == rCompare
.getCloseFront()
491 && getCloseBack() == rCompare
.getCloseBack());
497 basegfx::B3DRange
SdrExtrudePrimitive3D::getB3DRange(const geometry::ViewInformation3D
& /*rViewInformation*/) const
499 // use defaut from sdrPrimitive3D which uses transformation expanded by line width/2
500 // The parent implementation which uses the ranges of the decomposition would be more
501 // corrcet, but for historical reasons it is necessary to do the old method: To get
502 // the range of the non-transformed geometry and transform it then. This leads to different
503 // ranges where the new method is more correct, but the need to keep the old behaviour
504 // has priority here.
505 return get3DRangeFromSlices(getSlices());
508 Primitive3DSequence
SdrExtrudePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D
& rViewInformation
) const
510 if(getSdr3DObjectAttribute().getReducedLineGeometry())
512 if(!mpLastRLGViewInformation
||
513 (getLocalDecomposition().hasElements()
514 && *mpLastRLGViewInformation
!= rViewInformation
))
516 // conditions of last local decomposition with reduced lines have changed. Remember
517 // new one and clear current decompositiopn
518 ::osl::Mutex m_mutex
;
519 SdrExtrudePrimitive3D
* pThat
= const_cast< SdrExtrudePrimitive3D
* >(this);
520 pThat
->setLocalDecomposition(Primitive3DSequence());
521 delete pThat
->mpLastRLGViewInformation
;
522 pThat
->mpLastRLGViewInformation
= new geometry::ViewInformation3D(rViewInformation
);
526 // no test for buffering needed, call parent
527 return SdrPrimitive3D::get3DDecomposition(rViewInformation
);
531 ImplPrimitrive3DIDBlock(SdrExtrudePrimitive3D
, PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D
)
533 } // end of namespace primitive3d
534 } // end of namespace drawinglayer
536 //////////////////////////////////////////////////////////////////////////////