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/sdrextrudeprimitive3d.hxx>
30 #include <basegfx/matrix/b2dhommatrix.hxx>
31 #include <basegfx/polygon/b2dpolygontools.hxx>
32 #include <basegfx/polygon/b3dpolypolygontools.hxx>
33 #include <drawinglayer/primitive3d/sdrdecompositiontools3d.hxx>
34 #include <basegfx/tools/canvastools.hxx>
35 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
36 #include <drawinglayer/geometry/viewinformation3d.hxx>
37 #include <drawinglayer/attribute/sdrfillattribute.hxx>
38 #include <drawinglayer/attribute/sdrlineattribute.hxx>
39 #include <drawinglayer/attribute/sdrshadowattribute.hxx>
41 //////////////////////////////////////////////////////////////////////////////
43 using namespace com::sun::star
;
45 //////////////////////////////////////////////////////////////////////////////
47 namespace drawinglayer
51 Primitive3DSequence
SdrExtrudePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D
& rViewInformation
) const
53 Primitive3DSequence aRetval
;
56 const Slice3DVector
& rSliceVector
= getSlices();
58 if(!rSliceVector
.empty())
62 // decide what to create
63 const ::com::sun::star::drawing::NormalsKind
eNormalsKind(getSdr3DObjectAttribute().getNormalsKind());
64 const bool bCreateNormals(::com::sun::star::drawing::NormalsKind_SPECIFIC
== eNormalsKind
);
65 const bool bCreateTextureCoordiantesX(::com::sun::star::drawing::TextureProjectionMode_OBJECTSPECIFIC
== getSdr3DObjectAttribute().getTextureProjectionX());
66 const bool bCreateTextureCoordiantesY(::com::sun::star::drawing::TextureProjectionMode_OBJECTSPECIFIC
== getSdr3DObjectAttribute().getTextureProjectionY());
67 basegfx::B2DHomMatrix aTexTransform
;
69 if(!getSdrLFSAttribute().getFill().isDefault() && (bCreateTextureCoordiantesX
|| bCreateTextureCoordiantesY
))
71 const basegfx::B2DPolygon
aFirstPolygon(maCorrectedPolyPolygon
.getB2DPolygon(0L));
72 const double fFrontLength(basegfx::tools::getLength(aFirstPolygon
));
73 const double fFrontArea(basegfx::tools::getArea(aFirstPolygon
));
74 const double fSqrtFrontArea(sqrt(fFrontArea
));
75 double fRelativeTextureWidth
= basegfx::fTools::equalZero(fSqrtFrontArea
) ? 1.0 : fFrontLength
/ fSqrtFrontArea
;
76 fRelativeTextureWidth
= (double)((sal_uInt32
)(fRelativeTextureWidth
- 0.5));
78 if(fRelativeTextureWidth
< 1.0)
80 fRelativeTextureWidth
= 1.0;
83 aTexTransform
.translate(-0.5, -0.5);
84 aTexTransform
.scale(-1.0, -1.0);
85 aTexTransform
.translate(0.5, 0.5);
86 aTexTransform
.scale(fRelativeTextureWidth
, 1.0);
90 ::std::vector
< basegfx::B3DPolyPolygon
> aFill
;
91 extractPlanesFromSlice(aFill
, rSliceVector
,
92 bCreateNormals
, getSmoothHorizontalNormals(), getSmoothNormals(), getSmoothLids(), false,
93 0.5, 0.6, bCreateTextureCoordiantesX
|| bCreateTextureCoordiantesY
, aTexTransform
);
96 const basegfx::B3DRange
aRange(getRangeFrom3DGeometry(aFill
));
99 if(!getSdrLFSAttribute().getFill().isDefault())
101 if(::com::sun::star::drawing::NormalsKind_SPHERE
== eNormalsKind
)
103 applyNormalsKindSphereTo3DGeometry(aFill
, aRange
);
105 else if(::com::sun::star::drawing::NormalsKind_FLAT
== eNormalsKind
)
107 applyNormalsKindFlatTo3DGeometry(aFill
);
110 if(getSdr3DObjectAttribute().getNormalsInvert())
112 applyNormalsInvertTo3DGeometry(aFill
);
116 // texture coordinates
117 if(!getSdrLFSAttribute().getFill().isDefault())
119 applyTextureTo3DGeometry(
120 getSdr3DObjectAttribute().getTextureProjectionX(),
121 getSdr3DObjectAttribute().getTextureProjectionY(),
127 if(!getSdrLFSAttribute().getFill().isDefault())
130 aRetval
= create3DPolyPolygonFillPrimitives(
134 getSdr3DObjectAttribute(),
135 getSdrLFSAttribute().getFill(),
136 getSdrLFSAttribute().getFillFloatTransGradient());
140 // create simplified 3d hit test geometry
141 aRetval
= createHiddenGeometryPrimitives3D(
145 getSdr3DObjectAttribute());
149 if(!getSdrLFSAttribute().getLine().isDefault())
151 if(getSdr3DObjectAttribute().getReducedLineGeometry())
153 // create geometric outlines with reduced line geometry for chart.
154 const basegfx::B3DPolyPolygon
aVerLine(extractVerticalLinesFromSlice(rSliceVector
));
155 const sal_uInt32
nCount(aVerLine
.count());
156 basegfx::B3DPolyPolygon aReducedLoops
;
157 basegfx::B3DPolyPolygon aNewLineGeometry
;
159 // sort out doubles (front and back planes when no edge rounding is done). Since
160 // this is a line geometry merged from PolyPolygons, loop over all Polygons
161 for(a
= 0; a
< nCount
; a
++)
163 const sal_uInt32
nReducedCount(aReducedLoops
.count());
164 const basegfx::B3DPolygon
aCandidate(aVerLine
.getB3DPolygon(a
));
169 for(sal_uInt32
b(0); bAdd
&& b
< nReducedCount
; b
++)
171 if(aCandidate
== aReducedLoops
.getB3DPolygon(b
))
180 aReducedLoops
.append(aCandidate
);
184 // from here work with reduced loops and reduced count without changing them
185 const sal_uInt32
nReducedCount(aReducedLoops
.count());
187 if(nReducedCount
> 1)
189 for(sal_uInt32
b(1); b
< nReducedCount
; b
++)
192 const basegfx::B3DPolygon
aCandA(aReducedLoops
.getB3DPolygon(b
- 1));
193 const basegfx::B3DPolygon
aCandB(aReducedLoops
.getB3DPolygon(b
));
195 // for each loop pair create the connection edges
196 createReducedOutlines(
205 // add reduced loops themselves
206 aNewLineGeometry
.append(aReducedLoops
);
208 // to create vertical edges at non-C1/C2 steady loops, use maCorrectedPolyPolygon
209 // directly since the 3D Polygons do not suport this.
211 // Unfortunately there is no bezier polygon provided by the chart module; one reason is
212 // that the API for extrude wants a 3D polygon geometry (for historical reasons, i guess)
213 // and those have no beziers. Another reason is that he chart module uses self-created
214 // stuff to create the 2D geometry (in ShapeFactory::createPieSegment), but this geometry
215 // does not contain bezier infos, either. The only way which is possible for now is to 'detect'
216 // candidates for vertical edges of pie segments by looking for the angles in the polygon.
218 // This is all not very well designed ATM. Ideally, the ReducedLineGeometry is responsible
219 // for creating the outer geometry edges (createReducedOutlines), but for special edges
220 // like the vertical ones for pie center and both start/end, the incarnation with the
221 // knowledge about that it needs to create those and IS a pie segment -> in this case,
223 const sal_uInt32
nPolyCount(maCorrectedPolyPolygon
.count());
225 for(sal_uInt32
c(0); c
< nPolyCount
; c
++)
227 const basegfx::B2DPolygon
aCandidate(maCorrectedPolyPolygon
.getB2DPolygon(c
));
228 const sal_uInt32
nPointCount(aCandidate
.count());
232 sal_uInt32
nIndexA(nPointCount
);
233 sal_uInt32
nIndexB(nPointCount
);
234 sal_uInt32
nIndexC(nPointCount
);
236 for(sal_uInt32
d(0); d
< nPointCount
; d
++)
238 const sal_uInt32
nPrevInd((d
+ nPointCount
- 1) % nPointCount
);
239 const sal_uInt32
nNextInd((d
+ 1) % nPointCount
);
240 const basegfx::B2DPoint
aPoint(aCandidate
.getB2DPoint(d
));
241 const basegfx::B2DVector
aPrev(aCandidate
.getB2DPoint(nPrevInd
) - aPoint
);
242 const basegfx::B2DVector
aNext(aCandidate
.getB2DPoint(nNextInd
) - aPoint
);
243 const double fAngle(aPrev
.angle(aNext
));
245 // take each angle which deviates more than 10% from going straight as
246 // special edge. This will detect the two outer edges of pie segments,
247 // but not always the center one (think about a near 180 degree pie)
248 if(F_PI
- fabs(fAngle
) > F_PI
* 0.1)
250 if(nPointCount
== nIndexA
)
254 else if(nPointCount
== nIndexB
)
258 else if(nPointCount
== nIndexC
)
266 const bool bIndexAUsed(nIndexA
!= nPointCount
);
267 const bool bIndexBUsed(nIndexB
!= nPointCount
);
268 bool bIndexCUsed(nIndexC
!= nPointCount
);
272 // already three special edges found, so the center one was already detected
273 // and does not need to be searched
275 else if(bIndexAUsed
&& bIndexBUsed
)
277 // outer edges detected (they are approx. 90 degrees), but center one not.
278 // Look with the knowledge that it's in-between the two found ones
279 if(((nIndexA
+ 2) % nPointCount
) == nIndexB
)
281 nIndexC
= (nIndexA
+ 1) % nPointCount
;
283 else if(((nIndexA
+ nPointCount
- 2) % nPointCount
) == nIndexB
)
285 nIndexC
= (nIndexA
+ nPointCount
- 1) % nPointCount
;
288 bIndexCUsed
= (nIndexC
!= nPointCount
);
293 const basegfx::B2DPoint
aPoint(aCandidate
.getB2DPoint(nIndexA
));
294 const basegfx::B3DPoint
aStart(aPoint
.getX(), aPoint
.getY(), 0.0);
295 const basegfx::B3DPoint
aEnd(aPoint
.getX(), aPoint
.getY(), getDepth());
296 basegfx::B3DPolygon aToBeAdded
;
298 aToBeAdded
.append(aStart
);
299 aToBeAdded
.append(aEnd
);
300 aNewLineGeometry
.append(aToBeAdded
);
305 const basegfx::B2DPoint
aPoint(aCandidate
.getB2DPoint(nIndexB
));
306 const basegfx::B3DPoint
aStart(aPoint
.getX(), aPoint
.getY(), 0.0);
307 const basegfx::B3DPoint
aEnd(aPoint
.getX(), aPoint
.getY(), getDepth());
308 basegfx::B3DPolygon aToBeAdded
;
310 aToBeAdded
.append(aStart
);
311 aToBeAdded
.append(aEnd
);
312 aNewLineGeometry
.append(aToBeAdded
);
317 const basegfx::B2DPoint
aPoint(aCandidate
.getB2DPoint(nIndexC
));
318 const basegfx::B3DPoint
aStart(aPoint
.getX(), aPoint
.getY(), 0.0);
319 const basegfx::B3DPoint
aEnd(aPoint
.getX(), aPoint
.getY(), getDepth());
320 basegfx::B3DPolygon aToBeAdded
;
322 aToBeAdded
.append(aStart
);
323 aToBeAdded
.append(aEnd
);
324 aNewLineGeometry
.append(aToBeAdded
);
329 // append loops themselves
330 aNewLineGeometry
.append(aReducedLoops
);
332 if(aNewLineGeometry
.count())
334 const Primitive3DSequence
aLines(create3DPolyPolygonLinePrimitives(
335 aNewLineGeometry
, getTransform(), getSdrLFSAttribute().getLine()));
336 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval
, aLines
);
341 // extract line geometry from slices
342 const basegfx::B3DPolyPolygon
aHorLine(extractHorizontalLinesFromSlice(rSliceVector
, false));
343 const basegfx::B3DPolyPolygon
aVerLine(extractVerticalLinesFromSlice(rSliceVector
));
345 // add horizontal lines
346 const Primitive3DSequence
aHorLines(create3DPolyPolygonLinePrimitives(
347 aHorLine
, getTransform(), getSdrLFSAttribute().getLine()));
348 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval
, aHorLines
);
350 // add vertical lines
351 const Primitive3DSequence
aVerLines(create3DPolyPolygonLinePrimitives(
352 aVerLine
, getTransform(), getSdrLFSAttribute().getLine()));
353 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval
, aVerLines
);
358 if(!getSdrLFSAttribute().getShadow().isDefault() && aRetval
.hasElements())
360 const Primitive3DSequence
aShadow(createShadowPrimitive3D(
361 aRetval
, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
362 appendPrimitive3DSequenceToPrimitive3DSequence(aRetval
, aShadow
);
369 void SdrExtrudePrimitive3D::impCreateSlices()
371 // prepare the polygon. No double points, correct orientations and a correct
372 // outmost polygon are needed
373 maCorrectedPolyPolygon
= getPolyPolygon();
374 maCorrectedPolyPolygon
.removeDoublePoints();
375 maCorrectedPolyPolygon
= basegfx::tools::correctOrientations(maCorrectedPolyPolygon
);
376 maCorrectedPolyPolygon
= basegfx::tools::correctOutmostPolygon(maCorrectedPolyPolygon
);
378 // prepare slices as geometry
379 createExtrudeSlices(maSlices
, maCorrectedPolyPolygon
, getBackScale(), getDiagonal(), getDepth(), getCharacterMode(), getCloseFront(), getCloseBack());
382 const Slice3DVector
& SdrExtrudePrimitive3D::getSlices() const
384 // This can be made dependent of getSdrLFSAttribute().getFill() and getSdrLFSAttribute().getLine()
385 // again when no longer geometry is needed for non-visible 3D objects as it is now for chart
386 if(getPolyPolygon().count() && !maSlices
.size())
388 ::osl::Mutex m_mutex
;
389 const_cast< SdrExtrudePrimitive3D
& >(*this).impCreateSlices();
395 SdrExtrudePrimitive3D::SdrExtrudePrimitive3D(
396 const basegfx::B3DHomMatrix
& rTransform
,
397 const basegfx::B2DVector
& rTextureSize
,
398 const attribute::SdrLineFillShadowAttribute3D
& rSdrLFSAttribute
,
399 const attribute::Sdr3DObjectAttribute
& rSdr3DObjectAttribute
,
400 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
405 bool bSmoothHorizontalNormals
,
410 : SdrPrimitive3D(rTransform
, rTextureSize
, rSdrLFSAttribute
, rSdr3DObjectAttribute
),
411 maCorrectedPolyPolygon(),
413 maPolyPolygon(rPolyPolygon
),
415 mfDiagonal(fDiagonal
),
416 mfBackScale(fBackScale
),
417 mpLastRLGViewInformation(0),
418 mbSmoothNormals(bSmoothNormals
),
419 mbSmoothHorizontalNormals(bSmoothHorizontalNormals
),
420 mbSmoothLids(bSmoothLids
),
421 mbCharacterMode(bCharacterMode
),
422 mbCloseFront(bCloseFront
),
423 mbCloseBack(bCloseBack
)
425 // make sure depth is positive
426 if(basegfx::fTools::lessOrEqual(getDepth(), 0.0))
431 // make sure the percentage value getDiagonal() is between 0.0 and 1.0
432 if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0))
436 else if(basegfx::fTools::moreOrEqual(getDiagonal(), 1.0))
441 // no close front/back when polygon is not closed
442 if(getPolyPolygon().count() && !getPolyPolygon().getB2DPolygon(0L).isClosed())
444 mbCloseFront
= mbCloseBack
= false;
447 // no edge rounding when not closing
448 if(!getCloseFront() && !getCloseBack())
454 SdrExtrudePrimitive3D::~SdrExtrudePrimitive3D()
456 if(mpLastRLGViewInformation
)
458 delete mpLastRLGViewInformation
;
462 bool SdrExtrudePrimitive3D::operator==(const BasePrimitive3D
& rPrimitive
) const
464 if(SdrPrimitive3D::operator==(rPrimitive
))
466 const SdrExtrudePrimitive3D
& rCompare
= static_cast< const SdrExtrudePrimitive3D
& >(rPrimitive
);
468 return (getPolyPolygon() == rCompare
.getPolyPolygon()
469 && getDepth() == rCompare
.getDepth()
470 && getDiagonal() == rCompare
.getDiagonal()
471 && getBackScale() == rCompare
.getBackScale()
472 && getSmoothNormals() == rCompare
.getSmoothNormals()
473 && getSmoothHorizontalNormals() == rCompare
.getSmoothHorizontalNormals()
474 && getSmoothLids() == rCompare
.getSmoothLids()
475 && getCharacterMode() == rCompare
.getCharacterMode()
476 && getCloseFront() == rCompare
.getCloseFront()
477 && getCloseBack() == rCompare
.getCloseBack());
483 basegfx::B3DRange
SdrExtrudePrimitive3D::getB3DRange(const geometry::ViewInformation3D
& /*rViewInformation*/) const
485 // use defaut from sdrPrimitive3D which uses transformation expanded by line width/2
486 // The parent implementation which uses the ranges of the decomposition would be more
487 // corrcet, but for historical reasons it is necessary to do the old method: To get
488 // the range of the non-transformed geometry and transform it then. This leads to different
489 // ranges where the new method is more correct, but the need to keep the old behaviour
490 // has priority here.
491 return get3DRangeFromSlices(getSlices());
494 Primitive3DSequence
SdrExtrudePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D
& rViewInformation
) const
496 if(getSdr3DObjectAttribute().getReducedLineGeometry())
498 if(!mpLastRLGViewInformation
||
499 (getBuffered3DDecomposition().hasElements()
500 && *mpLastRLGViewInformation
!= rViewInformation
))
502 // conditions of last local decomposition with reduced lines have changed. Remember
503 // new one and clear current decompositiopn
504 ::osl::Mutex m_mutex
;
505 SdrExtrudePrimitive3D
* pThat
= const_cast< SdrExtrudePrimitive3D
* >(this);
506 pThat
->setBuffered3DDecomposition(Primitive3DSequence());
507 delete pThat
->mpLastRLGViewInformation
;
508 pThat
->mpLastRLGViewInformation
= new geometry::ViewInformation3D(rViewInformation
);
512 // no test for buffering needed, call parent
513 return SdrPrimitive3D::get3DDecomposition(rViewInformation
);
517 ImplPrimitrive3DIDBlock(SdrExtrudePrimitive3D
, PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D
)
519 } // end of namespace primitive3d
520 } // end of namespace drawinglayer
522 //////////////////////////////////////////////////////////////////////////////
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */