1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_chart2.hxx"
26 
27 #include "ThreeDHelper.hxx"
28 #include "macros.hxx"
29 #include "DiagramHelper.hxx"
30 #include "ChartTypeHelper.hxx"
31 #include "BaseGFXHelper.hxx"
32 #include "DataSeriesHelper.hxx"
33 #include <editeng/unoprnms.hxx>
34 #include <com/sun/star/beans/XPropertyState.hpp>
35 #include <com/sun/star/chart2/XDiagram.hpp>
36 #include <com/sun/star/drawing/LineStyle.hpp>
37 
38 #include <tools/debug.hxx>
39 
40 //.............................................................................
41 namespace chart
42 {
43 //.............................................................................
44 using namespace ::com::sun::star;
45 using namespace ::com::sun::star::chart2;
46 
47 using ::com::sun::star::uno::Reference;
48 using ::com::sun::star::uno::Sequence;
49 using ::rtl::OUString;
50 using ::rtl::math::cos;
51 using ::rtl::math::sin;
52 using ::rtl::math::tan;
53 
54 #define FIXED_SIZE_FOR_3D_CHART_VOLUME (10000.0)
55 
56 namespace
57 {
58 
lcl_isRightAngledAxesSetAndSupported(const Reference<beans::XPropertySet> & xSceneProperties)59 bool lcl_isRightAngledAxesSetAndSupported( const Reference< beans::XPropertySet >& xSceneProperties )
60 {
61     sal_Bool bRightAngledAxes = sal_False;
62     if( xSceneProperties.is() )
63     {
64         xSceneProperties->getPropertyValue( C2U("RightAngledAxes")) >>= bRightAngledAxes;
65         if(bRightAngledAxes)
66         {
67             uno::Reference< chart2::XDiagram > xDiagram( xSceneProperties, uno::UNO_QUERY );
68             if( ChartTypeHelper::isSupportingRightAngledAxes(
69                     DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) ) )
70             {
71                 return true;
72             }
73         }
74     }
75     return false;
76 }
77 
lcl_RotateLightSource(const Reference<beans::XPropertySet> & xSceneProperties,const OUString & rLightSourceDirection,const OUString & rLightSourceOn,const::basegfx::B3DHomMatrix & rRotationMatrix)78 void lcl_RotateLightSource( const Reference< beans::XPropertySet >& xSceneProperties
79                            , const OUString& rLightSourceDirection
80                            , const OUString& rLightSourceOn
81                            , const ::basegfx::B3DHomMatrix& rRotationMatrix )
82 {
83     if( xSceneProperties.is() )
84     {
85         sal_Bool bLightOn = sal_False;
86         if( xSceneProperties->getPropertyValue( rLightSourceOn ) >>= bLightOn )
87         {
88             if( bLightOn )
89             {
90                 drawing::Direction3D aLight;
91                 if( xSceneProperties->getPropertyValue( rLightSourceDirection ) >>= aLight )
92                 {
93                     ::basegfx::B3DVector aLightVector( BaseGFXHelper::Direction3DToB3DVector( aLight ) );
94                     aLightVector = rRotationMatrix*aLightVector;
95 
96                     xSceneProperties->setPropertyValue( rLightSourceDirection
97                         , uno::makeAny( BaseGFXHelper::B3DVectorToDirection3D( aLightVector ) ) );
98                 }
99             }
100         }
101     }
102 }
103 
lcl_rotateLights(const::basegfx::B3DHomMatrix & rLightRottion,const Reference<beans::XPropertySet> & xSceneProperties)104 void lcl_rotateLights( const ::basegfx::B3DHomMatrix& rLightRottion, const Reference< beans::XPropertySet >& xSceneProperties )
105 {
106     if(!xSceneProperties.is())
107         return;
108 
109     ::basegfx::B3DHomMatrix aLightRottion( rLightRottion );
110     BaseGFXHelper::ReduceToRotationMatrix( aLightRottion );
111 
112     lcl_RotateLightSource( xSceneProperties, C2U("D3DSceneLightDirection1"), C2U("D3DSceneLightOn1"), aLightRottion );
113     lcl_RotateLightSource( xSceneProperties, C2U("D3DSceneLightDirection2"), C2U("D3DSceneLightOn2"), aLightRottion );
114     lcl_RotateLightSource( xSceneProperties, C2U("D3DSceneLightDirection3"), C2U("D3DSceneLightOn3"), aLightRottion );
115     lcl_RotateLightSource( xSceneProperties, C2U("D3DSceneLightDirection4"), C2U("D3DSceneLightOn4"), aLightRottion );
116     lcl_RotateLightSource( xSceneProperties, C2U("D3DSceneLightDirection5"), C2U("D3DSceneLightOn5"), aLightRottion );
117     lcl_RotateLightSource( xSceneProperties, C2U("D3DSceneLightDirection6"), C2U("D3DSceneLightOn6"), aLightRottion );
118     lcl_RotateLightSource( xSceneProperties, C2U("D3DSceneLightDirection7"), C2U("D3DSceneLightOn7"), aLightRottion );
119     lcl_RotateLightSource( xSceneProperties, C2U("D3DSceneLightDirection8"), C2U("D3DSceneLightOn8"), aLightRottion );
120 }
121 
lcl_getInverseRotationMatrix(const Reference<beans::XPropertySet> & xSceneProperties)122 ::basegfx::B3DHomMatrix lcl_getInverseRotationMatrix( const Reference< beans::XPropertySet >& xSceneProperties )
123 {
124     ::basegfx::B3DHomMatrix aInverseRotation;
125     double fXAngleRad=0.0;
126     double fYAngleRad=0.0;
127     double fZAngleRad=0.0;
128     ThreeDHelper::getRotationAngleFromDiagram(
129         xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
130     aInverseRotation.rotate( 0.0, 0.0, -fZAngleRad );
131     aInverseRotation.rotate( 0.0, -fYAngleRad, 0.0 );
132     aInverseRotation.rotate( -fXAngleRad, 0.0, 0.0 );
133     return aInverseRotation;
134 }
135 
lcl_getCompleteRotationMatrix(const Reference<beans::XPropertySet> & xSceneProperties)136 ::basegfx::B3DHomMatrix lcl_getCompleteRotationMatrix( const Reference< beans::XPropertySet >& xSceneProperties )
137 {
138     ::basegfx::B3DHomMatrix aCompleteRotation;
139     double fXAngleRad=0.0;
140     double fYAngleRad=0.0;
141     double fZAngleRad=0.0;
142     ThreeDHelper::getRotationAngleFromDiagram(
143         xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
144     aCompleteRotation.rotate( fXAngleRad, fYAngleRad, fZAngleRad );
145     return aCompleteRotation;
146 }
147 
lcl_isEqual(const drawing::Direction3D & rA,const drawing::Direction3D & rB)148 bool lcl_isEqual( const drawing::Direction3D& rA, const drawing::Direction3D& rB )
149 {
150     return ::rtl::math::approxEqual(rA.DirectionX, rB.DirectionX)
151         && ::rtl::math::approxEqual(rA.DirectionY, rB.DirectionY)
152         && ::rtl::math::approxEqual(rA.DirectionZ, rB.DirectionZ);
153 }
154 
lcl_isLightScheme(const uno::Reference<beans::XPropertySet> & xDiagramProps,bool bRealistic)155 bool lcl_isLightScheme( const uno::Reference< beans::XPropertySet >& xDiagramProps, bool bRealistic )
156 {
157     if(!xDiagramProps.is())
158         return false;
159 
160     sal_Bool bIsOn = sal_False;
161     xDiagramProps->getPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTON_2 ) ) >>= bIsOn;
162     if(!bIsOn)
163         return false;
164 
165     uno::Reference< chart2::XDiagram > xDiagram( xDiagramProps, uno::UNO_QUERY );
166     uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
167 
168     sal_Int32 nColor = 0;
169     xDiagramProps->getPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTCOLOR_2 ) ) >>= nColor;
170     if( nColor != ::chart::ChartTypeHelper::getDefaultDirectLightColor( !bRealistic, xChartType ) )
171         return false;
172 
173     sal_Int32 nAmbientColor = 0;
174     xDiagramProps->getPropertyValue( C2U( UNO_NAME_3D_SCENE_AMBIENTCOLOR ) ) >>= nAmbientColor;
175     if( nAmbientColor != ::chart::ChartTypeHelper::getDefaultAmbientLightColor( !bRealistic, xChartType ) )
176         return false;
177 
178     drawing::Direction3D aDirection(0,0,0);
179     xDiagramProps->getPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTDIRECTION_2 ) ) >>= aDirection;
180 
181     drawing::Direction3D aDefaultDirection( bRealistic
182         ? ChartTypeHelper::getDefaultRealisticLightDirection(xChartType)
183         : ChartTypeHelper::getDefaultSimpleLightDirection(xChartType) );
184 
185     //rotate default light direction when right angled axes are off but supported
186     {
187         sal_Bool bRightAngledAxes = sal_False;
188         xDiagramProps->getPropertyValue( C2U("RightAngledAxes")) >>= bRightAngledAxes;
189         if(!bRightAngledAxes)
190         {
191             if( ChartTypeHelper::isSupportingRightAngledAxes(
192                     DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) ) )
193             {
194                 ::basegfx::B3DHomMatrix aRotation( lcl_getCompleteRotationMatrix( xDiagramProps ) );
195                 BaseGFXHelper::ReduceToRotationMatrix( aRotation );
196                 ::basegfx::B3DVector aLightVector( BaseGFXHelper::Direction3DToB3DVector( aDefaultDirection ) );
197                 aLightVector = aRotation*aLightVector;
198                 aDefaultDirection = BaseGFXHelper::B3DVectorToDirection3D( aLightVector );
199             }
200         }
201     }
202 
203     return lcl_isEqual( aDirection, aDefaultDirection );
204 }
205 
lcl_isRealisticLightScheme(const uno::Reference<beans::XPropertySet> & xDiagramProps)206 bool lcl_isRealisticLightScheme( const uno::Reference< beans::XPropertySet >& xDiagramProps )
207 {
208     return lcl_isLightScheme( xDiagramProps, true /*bRealistic*/ );
209 }
lcl_isSimpleLightScheme(const uno::Reference<beans::XPropertySet> & xDiagramProps)210 bool lcl_isSimpleLightScheme( const uno::Reference< beans::XPropertySet >& xDiagramProps )
211 {
212     return lcl_isLightScheme( xDiagramProps, false /*bRealistic*/ );
213 }
lcl_setLightsForScheme(const uno::Reference<beans::XPropertySet> & xDiagramProps,const ThreeDLookScheme & rScheme)214 void lcl_setLightsForScheme( const uno::Reference< beans::XPropertySet >& xDiagramProps, const ThreeDLookScheme& rScheme )
215 {
216     if(!xDiagramProps.is())
217         return;
218     if( rScheme == ThreeDLookScheme_Unknown)
219         return;
220 
221     xDiagramProps->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTON_2 ), uno::makeAny( sal_True ) );
222 
223     uno::Reference< chart2::XDiagram > xDiagram( xDiagramProps, uno::UNO_QUERY );
224     uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
225     uno::Any aADirection( uno::makeAny( rScheme == ThreeDLookScheme_Simple
226         ? ChartTypeHelper::getDefaultSimpleLightDirection(xChartType)
227         : ChartTypeHelper::getDefaultRealisticLightDirection(xChartType) ) );
228 
229     xDiagramProps->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTDIRECTION_2 ), aADirection );
230     //rotate light direction when right angled axes are off but supported
231     {
232         sal_Bool bRightAngledAxes = sal_False;
233         xDiagramProps->getPropertyValue( C2U("RightAngledAxes")) >>= bRightAngledAxes;
234         if(!bRightAngledAxes)
235         {
236             if( ChartTypeHelper::isSupportingRightAngledAxes( xChartType ) )
237             {
238                 ::basegfx::B3DHomMatrix aRotation( lcl_getCompleteRotationMatrix( xDiagramProps ) );
239                 BaseGFXHelper::ReduceToRotationMatrix( aRotation );
240                 lcl_RotateLightSource( xDiagramProps, C2U("D3DSceneLightDirection2"), C2U("D3DSceneLightOn2"), aRotation );
241             }
242         }
243     }
244 
245     sal_Int32 nColor = ::chart::ChartTypeHelper::getDefaultDirectLightColor( rScheme==ThreeDLookScheme_Simple, xChartType );
246     xDiagramProps->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTCOLOR_2 ), uno::makeAny( nColor ) );
247 
248     sal_Int32 nAmbientColor = ::chart::ChartTypeHelper::getDefaultAmbientLightColor( rScheme==ThreeDLookScheme_Simple, xChartType );
249     xDiagramProps->setPropertyValue( C2U( UNO_NAME_3D_SCENE_AMBIENTCOLOR ), uno::makeAny( nAmbientColor ) );
250 }
251 
lcl_isRealisticScheme(drawing::ShadeMode aShadeMode,sal_Int32 nRoundedEdges,sal_Int32 nObjectLines)252 bool lcl_isRealisticScheme( drawing::ShadeMode aShadeMode
253                     , sal_Int32 nRoundedEdges
254                     , sal_Int32 nObjectLines )
255 {
256     if(aShadeMode!=drawing::ShadeMode_SMOOTH)
257         return false;
258     if(nRoundedEdges!=5)
259         return false;
260     if(nObjectLines!=0)
261         return false;
262     return true;
263 }
264 
lcl_isSimpleScheme(drawing::ShadeMode aShadeMode,sal_Int32 nRoundedEdges,sal_Int32 nObjectLines,const uno::Reference<XDiagram> & xDiagram)265 bool lcl_isSimpleScheme( drawing::ShadeMode aShadeMode
266                     , sal_Int32 nRoundedEdges
267                     , sal_Int32 nObjectLines
268                     , const uno::Reference< XDiagram >& xDiagram )
269 {
270     if(aShadeMode!=drawing::ShadeMode_FLAT)
271         return false;
272     if(nRoundedEdges!=0)
273         return false;
274     if(nObjectLines==0)
275     {
276         uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
277         return ChartTypeHelper::noBordersForSimpleScheme( xChartType );
278     }
279     if(nObjectLines!=1)
280         return false;
281     return true;
282 }
283 
lcl_setRealisticScheme(drawing::ShadeMode & rShadeMode,sal_Int32 & rnRoundedEdges,sal_Int32 & rnObjectLines)284 void lcl_setRealisticScheme( drawing::ShadeMode& rShadeMode
285                     , sal_Int32& rnRoundedEdges
286                     , sal_Int32& rnObjectLines )
287 {
288     rShadeMode = drawing::ShadeMode_SMOOTH;
289     rnRoundedEdges = 5;
290     rnObjectLines = 0;
291 }
292 
lcl_setSimpleScheme(drawing::ShadeMode & rShadeMode,sal_Int32 & rnRoundedEdges,sal_Int32 & rnObjectLines,const uno::Reference<XDiagram> & xDiagram)293 void lcl_setSimpleScheme( drawing::ShadeMode& rShadeMode
294                     , sal_Int32& rnRoundedEdges
295                     , sal_Int32& rnObjectLines
296                     , const uno::Reference< XDiagram >& xDiagram )
297 {
298     rShadeMode = drawing::ShadeMode_FLAT;
299     rnRoundedEdges = 0;
300 
301     uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
302     rnObjectLines = ChartTypeHelper::noBordersForSimpleScheme( xChartType ) ? 0 : 1;
303 }
304 
305 } //end anonymous namespace
306 
307 
getDefaultCameraGeometry(bool bPie)308 drawing::CameraGeometry ThreeDHelper::getDefaultCameraGeometry( bool bPie )
309 {
310     // ViewReferencePoint (Point on the View plane)
311     drawing::Position3D vrp(17634.6218373783, 10271.4823817647, 24594.8639082739);
312     // ViewPlaneNormal (Normal to the View Plane)
313     drawing::Direction3D vpn(0.416199821709347, 0.173649045905254, 0.892537795986984);
314     // ViewUpVector (determines the v-axis direction on the view plane as
315     // projection of VUP parallel to VPN onto th view pane)
316     drawing::Direction3D vup(-0.0733876362771618, 0.984807599917971, -0.157379306090273);
317 
318     if( bPie )
319     {
320         vrp = drawing::Position3D( 0.0, 0.0, 87591.2408759124 );//--> 5 percent perspecitve
321         vpn = drawing::Direction3D( 0.0, 0.0, 1.0 );
322         vup = drawing::Direction3D( 0.0, 1.0, 0.0 );
323     }
324 
325     return drawing::CameraGeometry( vrp, vpn, vup );
326 }
327 
328 namespace
329 {
lcl_getCameraMatrix(const uno::Reference<beans::XPropertySet> & xSceneProperties)330 ::basegfx::B3DHomMatrix lcl_getCameraMatrix( const uno::Reference< beans::XPropertySet >& xSceneProperties )
331 {
332     drawing::HomogenMatrix aCameraMatrix;
333 
334     drawing::CameraGeometry aCG( ThreeDHelper::getDefaultCameraGeometry() );
335     if( xSceneProperties.is() )
336         xSceneProperties->getPropertyValue( C2U( "D3DCameraGeometry" ) ) >>= aCG;
337 
338     ::basegfx::B3DVector aVPN( BaseGFXHelper::Direction3DToB3DVector( aCG.vpn ) );
339     ::basegfx::B3DVector aVUP( BaseGFXHelper::Direction3DToB3DVector( aCG.vup ) );
340 
341     //normalize vectors:
342     aVPN.normalize();
343     aVUP.normalize();
344 
345     ::basegfx::B3DVector aCross = ::basegfx::cross( aVUP, aVPN );
346 
347     //first line is VUP x VPN
348     aCameraMatrix.Line1.Column1 = aCross[0];
349     aCameraMatrix.Line1.Column2 = aCross[1];
350     aCameraMatrix.Line1.Column3 = aCross[2];
351     aCameraMatrix.Line1.Column4 = 0.0;
352 
353     //second line is VUP
354     aCameraMatrix.Line2.Column1 = aVUP[0];
355     aCameraMatrix.Line2.Column2 = aVUP[1];
356     aCameraMatrix.Line2.Column3 = aVUP[2];
357     aCameraMatrix.Line2.Column4 = 0.0;
358 
359     //third line is VPN
360     aCameraMatrix.Line3.Column1 = aVPN[0];
361     aCameraMatrix.Line3.Column2 = aVPN[1];
362     aCameraMatrix.Line3.Column3 = aVPN[2];
363     aCameraMatrix.Line3.Column4 = 0.0;
364 
365     //fourth line is 0 0 0 1
366     aCameraMatrix.Line4.Column1 = 0.0;
367     aCameraMatrix.Line4.Column2 = 0.0;
368     aCameraMatrix.Line4.Column3 = 0.0;
369     aCameraMatrix.Line4.Column4 = 1.0;
370 
371     return BaseGFXHelper::HomogenMatrixToB3DHomMatrix( aCameraMatrix );
372 }
373 
lcl_shiftAngleToIntervalMinusPiToPi(double fAngleRad)374 double lcl_shiftAngleToIntervalMinusPiToPi( double fAngleRad )
375 {
376     //valid range:  ]-Pi,Pi]
377     while( fAngleRad<=-F_PI )
378         fAngleRad+=(2*F_PI);
379     while( fAngleRad>F_PI )
380         fAngleRad-=(2*F_PI);
381     return fAngleRad;
382 }
383 
lcl_shiftAngleToIntervalMinus180To180(sal_Int32 & rnAngleDegree)384 void lcl_shiftAngleToIntervalMinus180To180( sal_Int32& rnAngleDegree )
385 {
386     //valid range:  ]-180,180]
387     while( rnAngleDegree<=-180 )
388         rnAngleDegree+=360;
389     while( rnAngleDegree>180 )
390         rnAngleDegree-=360;
391 }
392 
lcl_shiftAngleToIntervalZeroTo360(sal_Int32 & rnAngleDegree)393 void lcl_shiftAngleToIntervalZeroTo360( sal_Int32& rnAngleDegree )
394 {
395     //valid range:  [0,360[
396     while( rnAngleDegree<0 )
397         rnAngleDegree+=360;
398     while( rnAngleDegree>=360 )
399         rnAngleDegree-=360;
400 }
401 
lcl_ensureIntervalMinus1To1(double & rSinOrCos)402 void lcl_ensureIntervalMinus1To1( double& rSinOrCos )
403 {
404     if (rSinOrCos < -1.0)
405        rSinOrCos = -1.0;
406     else if (rSinOrCos > 1.0)
407         rSinOrCos = 1.0;
408 }
409 
lcl_isSinZero(double fAngleRad)410 bool lcl_isSinZero( double fAngleRad )
411 {
412     return ::basegfx::fTools::equalZero( sin(fAngleRad), 0.0000001 );
413 }
lcl_isCosZero(double fAngleRad)414 bool lcl_isCosZero( double fAngleRad )
415 {
416     return ::basegfx::fTools::equalZero( cos(fAngleRad), 0.0000001 );
417 }
418 
419 }
420 
convertElevationRotationDegToXYZAngleRad(sal_Int32 nElevationDeg,sal_Int32 nRotationDeg,double & rfXAngleRad,double & rfYAngleRad,double & rfZAngleRad)421 void ThreeDHelper::convertElevationRotationDegToXYZAngleRad(
422     sal_Int32 nElevationDeg, sal_Int32 nRotationDeg,
423     double& rfXAngleRad, double& rfYAngleRad, double& rfZAngleRad)
424 {
425     // for a description of the algorithm see issue 72994
426     //http://www.openoffice.org/issues/show_bug.cgi?id=72994
427     //http://www.openoffice.org/nonav/issues/showattachment.cgi/50608/DescriptionCorrected.odt
428 
429     lcl_shiftAngleToIntervalZeroTo360( nElevationDeg );
430     lcl_shiftAngleToIntervalZeroTo360( nRotationDeg );
431 
432     double& x = rfXAngleRad;
433     double& y = rfYAngleRad;
434     double& z = rfZAngleRad;
435 
436     double E = F_PI*nElevationDeg/180; //elevation in Rad
437     double R = F_PI*nRotationDeg/180; //rotation in Rad
438 
439     if( (nRotationDeg == 0 || nRotationDeg == 180 )
440         && ( nElevationDeg == 90 || nElevationDeg == 270 ) )
441     {
442         //sR==0 && cE==0
443         z = 0.0;
444         //element 23
445         double f23 = cos(R)*sin(E);
446         if(f23>0)
447             x = F_PI/2;
448         else
449             x = -F_PI/2;
450         y = R;
451     }
452     else if( ( nRotationDeg == 90 || nRotationDeg == 270 )
453         && ( nElevationDeg == 90 || nElevationDeg == 270 ) )
454     {
455         //cR==0 && cE==0
456         z = F_PI/2;
457         if( sin(R)>0 )
458             x = F_PI/2.0;
459         else
460             x = -F_PI/2.0;
461 
462         if( (sin(R)*sin(E))>0 )
463             y = 0.0;
464         else
465             y = F_PI;
466     }
467     else if( (nRotationDeg == 0 || nRotationDeg == 180 )
468         && ( nElevationDeg == 0 || nElevationDeg == 180 ) )
469     {
470         //sR==0 && sE==0
471         z = 0.0;
472         y = R;
473         x = E;
474     }
475     else if( ( nRotationDeg == 90 || nRotationDeg == 270 )
476         && ( nElevationDeg == 0 || nElevationDeg == 180 ) )
477     {
478         //cR==0 && sE==0
479         z = 0.0;
480 
481         if( (sin(R)/cos(E))>0 )
482             y = F_PI/2;
483         else
484             y = -F_PI/2;
485 
486         if( (cos(E))>0 )
487             x = 0;
488         else
489             x = F_PI;
490     }
491     else if ( nElevationDeg == 0 || nElevationDeg == 180 )
492     {
493         //sR!=0 cR!=0 sE==0
494         z = 0.0;
495         x = E;
496         y = R;
497         //use element 13 for sign
498         if((cos(x)*sin(y)*sin(R))<0.0)
499             y *= -1.0;
500     }
501     else if ( nElevationDeg == 90 || nElevationDeg == 270 )
502     {
503         //sR!=0 cR!=0 cE==0
504         //element 12 + 22 --> y=0 or F_PI and x=+-F_PI/2
505         //-->element 13/23:
506         z = atan(sin(R)/(cos(R)*sin(E)));
507         //use element 13 for sign for x
508         if( (sin(R)*sin(z))>0.0 )
509             x = F_PI/2;
510         else
511             x = -F_PI/2;
512         //use element 21 for y
513         if( (sin(R)*sin(E)*sin(z))>0.0)
514             y = 0.0;
515         else
516             y = F_PI;
517     }
518     else if ( nRotationDeg == 0 || nRotationDeg == 180 )
519     {
520         //sE!=0 cE!=0 sR==0
521         z = 0.0;
522         x = E;
523         y = R;
524         double f23 = cos(R)*sin(E);
525         if( (f23 * sin(x)) < 0.0 )
526             x *= -1.0; //todo ??
527     }
528     else if (nRotationDeg == 90 || nRotationDeg == 270)
529     {
530         //sE!=0 cE!=0 cR==0
531         //z = +- F_PI/2;
532         //x = +- F_PI/2;
533         z = F_PI/2;
534         x = F_PI/2;
535         double sR = sin(R);
536         if( sR<0.0 )
537             x *= -1.0; //different signs for x and z
538 
539         //use element 21:
540         double cy = sR*sin(E)/sin(z);
541         lcl_ensureIntervalMinus1To1(cy);
542         y = acos(cy);
543 
544         //use element 22 for sign:
545         if( (sin(x)*sin(y)*sin(z)*cos(E))<0.0)
546             y *= -1.0;
547     }
548     else
549     {
550         z = atan(tan(R) * sin(E));
551         if(cos(z)==0.0)
552         {
553             DBG_ERROR("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
554             return;
555         }
556         double cy = cos(R)/cos(z);
557         lcl_ensureIntervalMinus1To1(cy);
558         y = acos(cy);
559 
560         //element 12 in 23
561         double fDenominator = cos(z)*(1.0-pow(sin(y),2));
562         if(fDenominator==0.0)
563         {
564             DBG_ERROR("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
565             return;
566         }
567         double sx = cos(R)*sin(E)/fDenominator;
568         lcl_ensureIntervalMinus1To1(sx);
569         x = asin( sx );
570 
571         //use element 13 for sign:
572         double f13a = cos(x)*cos(z)*sin(y);
573         double f13b = sin(R)-sx*sin(z);
574         if( (f13b*f13a)<0.0 )
575         {
576             //change x or y
577             //use element 22 for further investigations:
578             //try
579             y *= -1;
580             double f22a = cos(x)*cos(z);
581             double f22b = cos(E)-(sx*sin(y)*sin(z));
582             if( (f22a*f22b)<0.0 )
583             {
584                 y *= -1;
585                 x=(F_PI-x);
586             }
587         }
588         else
589         {
590             //change nothing or both
591             //use element 22 for further investigations:
592             double f22a = cos(x)*cos(z);
593             double f22b = cos(E)-(sx*sin(y)*sin(z));
594             if( (f22a*f22b)<0.0 )
595             {
596                 y *= -1;
597                 x=(F_PI-x);
598             }
599         }
600     }
601 }
602 
convertXYZAngleRadToElevationRotationDeg(sal_Int32 & rnElevationDeg,sal_Int32 & rnRotationDeg,double fXRad,double fYRad,double fZRad)603 void ThreeDHelper::convertXYZAngleRadToElevationRotationDeg(
604     sal_Int32& rnElevationDeg, sal_Int32& rnRotationDeg,
605     double fXRad, double fYRad, double fZRad)
606 {
607     // for a description of the algorithm see issue 72994
608     //http://www.openoffice.org/issues/show_bug.cgi?id=72994
609     //http://www.openoffice.org/nonav/issues/showattachment.cgi/50608/DescriptionCorrected.odt
610 
611     double R = 0.0; //Rotation in Rad
612     double E = 0.0; //Elevation in Rad
613 
614     double& x = fXRad;
615     double& y = fYRad;
616     double& z = fZRad;
617 
618     double f11 = cos(y)*cos(z);
619 
620     if( lcl_isSinZero(y) )
621     {
622         //siny == 0
623 
624         if( lcl_isCosZero(x) )
625         {
626             //siny == 0 && cosx == 0
627 
628             if( lcl_isSinZero(z) )
629             {
630                 //siny == 0 && cosx == 0 && sinz == 0
631                 //example: x=+-90 y=0oder180 z=0(oder180)
632 
633                 //element 13+11
634                 if( f11 > 0 )
635                     R = 0.0;
636                 else
637                     R = F_PI;
638 
639                 //element 23
640                 double f23 = cos(z)*sin(x) / cos(R);
641                 if( f23 > 0 )
642                     E = F_PI/2.0;
643                 else
644                     E = -F_PI/2.0;
645             }
646             else if( lcl_isCosZero(z) )
647             {
648                 //siny == 0 && cosx == 0 && cosz == 0
649                 //example: x=+-90 y=0oder180 z=+-90
650 
651                 double f13 = sin(x)*sin(z);
652                 //element 13+11
653                 if( f13 > 0 )
654                     R = F_PI/2.0;
655                 else
656                     R = -F_PI/2.0;
657 
658                 //element 21
659                 double f21 = cos(y)*sin(z) / sin(R);
660                 if( f21 > 0 )
661                     E = F_PI/2.0;
662                 else
663                     E = -F_PI/2.0;
664             }
665             else
666             {
667                 //siny == 0 && cosx == 0 && cosz != 0 && sinz != 0
668                 //element 11 && 13
669                 double f13 = sin(x)*sin(z);
670                 R = atan( f13/f11 );
671 
672                 if(f11<0)
673                     R+=F_PI;
674 
675                 //element 23
676                 double f23 = cos(z)*sin(x);
677                 if( f23/cos(R) > 0 )
678                     E = F_PI/2.0;
679                 else
680                     E = -F_PI/2.0;
681             }
682         }
683         else if( lcl_isSinZero(x) )
684         {
685             //sinY==0 sinX==0
686             //element 13+11
687             if( f11 > 0 )
688                 R = 0.0;
689             else
690                 R = F_PI;
691 
692             double f22 = cos(x)*cos(z);
693             if( f22 > 0 )
694                 E = 0.0;
695             else
696                 E = F_PI;
697         }
698         else if( lcl_isSinZero(z) )
699         {
700             //sinY==0 sinZ==0 sinx!=0 cosx!=0
701             //element 13+11
702             if( f11 > 0 )
703                 R = 0.0;
704             else
705                 R = F_PI;
706 
707             //element 22 && 23
708             double f22 = cos(x)*cos(z);
709             double f23 = cos(z)*sin(x);
710             E = atan( f23/(f22*cos(R)) );
711             if( (f22*cos(E))<0 )
712                 E+=F_PI;
713         }
714         else if( lcl_isCosZero(z) )
715         {
716             //sinY == 0 && cosZ == 0 && cosx != 0 && sinx != 0
717             double f13 = sin(x)*sin(z);
718             //element 13+11
719             if( f13 > 0 )
720                 R = F_PI/2.0;
721             else
722                 R = -F_PI/2.0;
723 
724             //element 21+22
725             double f21 = cos(y)*sin(z);
726             if( f21/sin(R) > 0 )
727                 E = F_PI/2.0;
728             else
729                 E = -F_PI/2.0;
730         }
731         else
732         {
733             //sinY == 0 && all other !=0
734             double f13 = sin(x)*sin(z);
735             R = atan( f13/f11 );
736             if( (f11*cos(R))<0.0 )
737                 R+=F_PI;
738 
739             double f22 = cos(x)*cos(z);
740             if( !lcl_isCosZero(R) )
741                 E = atan( cos(z)*sin(x) /( f22*cos(R) ) );
742             else
743                 E = atan( cos(y)*sin(z) /( f22*sin(R) ) );
744             if( (f22*cos(E))<0 )
745                 E+=F_PI;
746         }
747     }
748     else if( lcl_isCosZero(y) )
749     {
750         //cosY==0
751 
752         double f13 = sin(x)*sin(z)+cos(x)*cos(z)*sin(y);
753         if( f13 >= 0 )
754             R = F_PI/2.0;
755         else
756             R = -F_PI/2.0;
757 
758         double f22 = cos(x)*cos(z)+sin(x)*sin(y)*sin(z);
759         if( f22 >= 0 )
760             E = 0.0;
761         else
762             E = F_PI;
763     }
764     else if( lcl_isSinZero(x) )
765     {
766         //cosY!=0 sinY!=0 sinX=0
767         if( lcl_isSinZero(z) )
768         {
769             //cosY!=0 sinY!=0 sinX=0 sinZ=0
770             double f13 = cos(x)*cos(z)*sin(y);
771             R = atan( f13/f11 );
772             //R = asin(f13);
773             if( f11<0 )
774                 R+=F_PI;
775 
776             double f22 = cos(x)*cos(z);
777             if( f22>0 )
778                 E = 0.0;
779             else
780                 E = F_PI;
781         }
782         else if( lcl_isCosZero(z) )
783         {
784             //cosY!=0 sinY!=0 sinX=0 cosZ=0
785             R = x;
786             E = y;//or -y
787             //use 23 for 'signs'
788             double f23 =  -1.0*cos(x)*sin(y)*sin(z);
789             if( (f23*cos(R)*sin(E))<0.0 )
790             {
791                 //change R or E
792                 E = -y;
793             }
794         }
795         else
796         {
797             //cosY!=0 sinY!=0 sinX=0 sinZ!=0 cosZ!=0
798             double f13 = cos(x)*cos(z)*sin(y);
799             R = atan( f13/f11 );
800 
801             if( f11<0 )
802                 R+=F_PI;
803 
804             double f21 = cos(y)*sin(z);
805             double f22 = cos(x)*cos(z);
806             E = atan(f21/(f22*sin(R)) );
807 
808             if( (f22*cos(E))<0.0 )
809                 E+=F_PI;
810         }
811     }
812     else if( lcl_isCosZero(x) )
813     {
814         //cosY!=0 sinY!=0 cosX=0
815 
816         if( lcl_isSinZero(z) )
817         {
818             //cosY!=0 sinY!=0 cosX=0 sinZ=0
819             R=0;//13 -> R=0 or F_PI
820             if( f11<0.0 )
821                 R=F_PI;
822             E=F_PI/2;//22 -> E=+-F_PI/2
823             //use element 11 and 23 for sign
824             double f23 = cos(z)*sin(x);
825             if( (f11*f23*sin(E))<0.0 )
826                 E=-F_PI/2.0;
827         }
828         else if( lcl_isCosZero(z) )
829         {
830             //cosY!=0 sinY!=0 cosX=0 cosZ=0
831             //element 11 & 13:
832             if( (sin(x)*sin(z))>0.0 )
833                 R=F_PI/2.0;
834             else
835                 R=-F_PI/2.0;
836             //element 22:
837             E=acos( sin(x)*sin(y)*sin(z));
838             //use element 21 for sign:
839             if( (cos(y)*sin(z)*sin(R)*sin(E))<0.0 )
840                 E*=-1.0;
841         }
842         else
843         {
844             //cosY!=0 sinY!=0 cosX=0 sinZ!=0 cosZ!=0
845             //element 13/11
846             R = atan( sin(x)*sin(z)/(cos(y)*cos(z)) );
847             //use 13 for 'sign'
848             if( (sin(x)*sin(z))<0.0 )
849                 R += F_PI;
850             //element 22
851             E = acos(sin(x)*sin(y)*sin(z) );
852             //use 21 for sign
853             if( (cos(y)*sin(z)*sin(R)*sin(E))<0.0 )
854                 E*=-1.0;
855         }
856     }
857     else if( lcl_isSinZero(z) )
858     {
859         //cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ=0
860         //element 11
861         R=y;
862         //use elenment 13 for sign
863         if( (cos(x)*cos(z)*sin(y)*sin(R))<0.0 )
864             R*=-1.0;
865         //element 22
866         E = acos( cos(x)*cos(z) );
867         //use element 23 for sign
868         if( (cos(z)*sin(x)*cos(R)*sin(E))<0.0 )
869             E*=-1.0;
870     }
871     else if( lcl_isCosZero(z) )
872     {
873         //cosY!=0 sinY!=0 sinX!=0 cosX!=0 cosZ=0
874         //element 21/23
875         R=atan(-cos(y)/(cos(x)*sin(y)));
876         //use element 13 for 'sign'
877         if( (sin(x)*sin(z)*sin(R))<0.0 )
878             R+=F_PI;
879         //element 21/22
880         E=atan( cos(y)*sin(z)/(sin(R)*sin(x)*sin(y)*sin(z)) );
881         //use element 23 for 'sign'
882         if( (-cos(x)*sin(y)*sin(z)*cos(R)*sin(E))<0.0 )
883             E+=F_PI;
884     }
885     else
886     {
887         //cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ!=0 cosZ!=0
888         //13/11:
889         double f13 = sin(x)*sin(z)+cos(x)*cos(z)*sin(y);
890         R = atan( f13/ f11 );
891         if(f11<0.0)
892             R+=F_PI;
893         double f22 = cos(x)*cos(z)+sin(x)*sin(y)*sin(z);
894         double f23 = cos(x)*sin(y)*sin(z)-cos(z)*sin(x);
895         //23/22:
896         E = atan( -1.0*f23/(f22*cos(R)) );
897         if(f22<0.0)
898             E+=F_PI;
899     }
900 
901     rnElevationDeg = ::basegfx::fround( BaseGFXHelper::Rad2Deg( E ) );
902     rnRotationDeg = ::basegfx::fround( BaseGFXHelper::Rad2Deg( R ) );
903 }
904 
getValueClippedToRange(double fAngle,const double & fPositivLimit)905 double ThreeDHelper::getValueClippedToRange( double fAngle, const double& fPositivLimit )
906 {
907     if( fAngle<-1*fPositivLimit )
908         fAngle=-1*fPositivLimit;
909     else if( fAngle>fPositivLimit )
910         fAngle=fPositivLimit;
911     return fAngle;
912 }
913 
getXDegreeAngleLimitForRightAngledAxes()914 double ThreeDHelper::getXDegreeAngleLimitForRightAngledAxes()
915 {
916     return 90.0;
917 }
918 
getYDegreeAngleLimitForRightAngledAxes()919 double ThreeDHelper::getYDegreeAngleLimitForRightAngledAxes()
920 {
921     return 45.0;
922 }
923 
adaptRadAnglesForRightAngledAxes(double & rfXAngleRad,double & rfYAngleRad)924 void ThreeDHelper::adaptRadAnglesForRightAngledAxes( double& rfXAngleRad, double& rfYAngleRad )
925 {
926     rfXAngleRad = ThreeDHelper::getValueClippedToRange(rfXAngleRad, BaseGFXHelper::Deg2Rad(ThreeDHelper::getXDegreeAngleLimitForRightAngledAxes()) );
927     rfYAngleRad = ThreeDHelper::getValueClippedToRange(rfYAngleRad, BaseGFXHelper::Deg2Rad(ThreeDHelper::getYDegreeAngleLimitForRightAngledAxes()) );
928 }
929 
getRotationAngleFromDiagram(const Reference<beans::XPropertySet> & xSceneProperties,double & rfXAngleRad,double & rfYAngleRad,double & rfZAngleRad)930 void ThreeDHelper::getRotationAngleFromDiagram(
931         const Reference< beans::XPropertySet >& xSceneProperties, double& rfXAngleRad, double& rfYAngleRad, double& rfZAngleRad )
932 {
933     //takes the camera and the transformation matrix into account
934 
935     rfXAngleRad = rfYAngleRad = rfZAngleRad = 0.0;
936 
937     if( !xSceneProperties.is() )
938         return;
939 
940     //get camera rotation
941     ::basegfx::B3DHomMatrix aFixCameraRotationMatrix( lcl_getCameraMatrix( xSceneProperties ) );
942     BaseGFXHelper::ReduceToRotationMatrix( aFixCameraRotationMatrix );
943 
944     //get scene rotation
945     ::basegfx::B3DHomMatrix aSceneRotation;
946     {
947         drawing::HomogenMatrix aHomMatrix;
948         if( xSceneProperties->getPropertyValue( C2U("D3DTransformMatrix")) >>= aHomMatrix )
949         {
950             aSceneRotation = BaseGFXHelper::HomogenMatrixToB3DHomMatrix( aHomMatrix );
951             BaseGFXHelper::ReduceToRotationMatrix( aSceneRotation );
952         }
953     }
954 
955     ::basegfx::B3DHomMatrix aResultRotation = aFixCameraRotationMatrix * aSceneRotation;
956     ::basegfx::B3DTuple aRotation( BaseGFXHelper::GetRotationFromMatrix( aResultRotation ) );
957 
958     rfXAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(aRotation.getX());
959     rfYAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(aRotation.getY());
960     rfZAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(aRotation.getZ());
961 
962     if(rfZAngleRad<(-F_PI/2) || rfZAngleRad>(F_PI/2))
963     {
964         rfZAngleRad-=F_PI;
965         rfXAngleRad-=F_PI;
966         rfYAngleRad=(F_PI-rfYAngleRad);
967 
968         rfXAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(rfXAngleRad);
969         rfYAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(rfYAngleRad);
970         rfZAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(rfZAngleRad);
971     }
972 }
973 
switchRightAngledAxes(const Reference<beans::XPropertySet> & xSceneProperties,sal_Bool bRightAngledAxes,bool bRotateLights)974 void ThreeDHelper::switchRightAngledAxes( const Reference< beans::XPropertySet >& xSceneProperties, sal_Bool bRightAngledAxes, bool bRotateLights )
975 {
976     try
977     {
978         if( xSceneProperties.is() )
979         {
980             sal_Bool bOldRightAngledAxes = sal_False;
981             xSceneProperties->getPropertyValue( C2U("RightAngledAxes")) >>= bOldRightAngledAxes;
982             if( bOldRightAngledAxes!=bRightAngledAxes)
983             {
984                 xSceneProperties->setPropertyValue( C2U("RightAngledAxes"), uno::makeAny( bRightAngledAxes ));
985                 if( bRotateLights )
986                 {
987                     if(bRightAngledAxes)
988                     {
989                         ::basegfx::B3DHomMatrix aInverseRotation( lcl_getInverseRotationMatrix( xSceneProperties ) );
990                         lcl_rotateLights( aInverseRotation, xSceneProperties );
991                     }
992                     else
993                     {
994                         ::basegfx::B3DHomMatrix aCompleteRotation( lcl_getCompleteRotationMatrix( xSceneProperties ) );
995                         lcl_rotateLights( aCompleteRotation, xSceneProperties );
996                     }
997                 }
998             }
999         }
1000     }
1001     catch( const uno::Exception & ex )
1002     {
1003         ASSERT_EXCEPTION( ex );
1004     }
1005 }
1006 
setRotationAngleToDiagram(const Reference<beans::XPropertySet> & xSceneProperties,double fXAngleRad,double fYAngleRad,double fZAngleRad)1007 void ThreeDHelper::setRotationAngleToDiagram(
1008     const Reference< beans::XPropertySet >& xSceneProperties
1009         , double fXAngleRad, double fYAngleRad, double fZAngleRad )
1010 {
1011     //the rotation of the camera is not touched but taken into account
1012     //the rotation difference is applied to the transformation matrix
1013 
1014     //the light sources will be adapted also
1015 
1016     if( !xSceneProperties.is() )
1017         return;
1018 
1019     try
1020     {
1021         //remind old rotation for adaption of light directions
1022         ::basegfx::B3DHomMatrix aInverseOldRotation( lcl_getInverseRotationMatrix( xSceneProperties ) );
1023 
1024         ::basegfx::B3DHomMatrix aInverseCameraRotation;
1025         {
1026             ::basegfx::B3DTuple aR( BaseGFXHelper::GetRotationFromMatrix(
1027                     lcl_getCameraMatrix( xSceneProperties ) ) );
1028             aInverseCameraRotation.rotate( 0.0, 0.0, -aR.getZ() );
1029             aInverseCameraRotation.rotate( 0.0, -aR.getY(), 0.0 );
1030             aInverseCameraRotation.rotate( -aR.getX(), 0.0, 0.0 );
1031         }
1032 
1033         ::basegfx::B3DHomMatrix aCumulatedRotation;
1034         aCumulatedRotation.rotate( fXAngleRad, fYAngleRad, fZAngleRad );
1035 
1036         //calculate new scene matrix
1037         ::basegfx::B3DHomMatrix aSceneRotation = aInverseCameraRotation*aCumulatedRotation;
1038         BaseGFXHelper::ReduceToRotationMatrix( aSceneRotation );
1039 
1040         //set new rotation to transformation matrix
1041         xSceneProperties->setPropertyValue(
1042             C2U("D3DTransformMatrix"), uno::makeAny( BaseGFXHelper::B3DHomMatrixToHomogenMatrix( aSceneRotation )));
1043 
1044         //rotate lights if RightAngledAxes are not set or not supported
1045         sal_Bool bRightAngledAxes = sal_False;
1046         xSceneProperties->getPropertyValue( C2U("RightAngledAxes")) >>= bRightAngledAxes;
1047         uno::Reference< chart2::XDiagram > xDiagram( xSceneProperties, uno::UNO_QUERY );
1048         if(!bRightAngledAxes || !ChartTypeHelper::isSupportingRightAngledAxes(
1049                     DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) ) )
1050         {
1051             ::basegfx::B3DHomMatrix aNewRotation;
1052             aNewRotation.rotate( fXAngleRad, fYAngleRad, fZAngleRad );
1053             lcl_rotateLights( aNewRotation*aInverseOldRotation, xSceneProperties );
1054         }
1055     }
1056     catch( const uno::Exception & ex )
1057     {
1058         ASSERT_EXCEPTION( ex );
1059     }
1060 }
1061 
getRotationFromDiagram(const uno::Reference<beans::XPropertySet> & xSceneProperties,sal_Int32 & rnHorizontalAngleDegree,sal_Int32 & rnVerticalAngleDegree)1062 void ThreeDHelper::getRotationFromDiagram( const uno::Reference< beans::XPropertySet >& xSceneProperties
1063             , sal_Int32& rnHorizontalAngleDegree, sal_Int32& rnVerticalAngleDegree )
1064 {
1065     double fXAngle, fYAngle, fZAngle;
1066     ThreeDHelper::getRotationAngleFromDiagram( xSceneProperties, fXAngle, fYAngle, fZAngle );
1067 
1068     if( !lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
1069     {
1070         ThreeDHelper::convertXYZAngleRadToElevationRotationDeg(
1071             rnHorizontalAngleDegree, rnVerticalAngleDegree, fXAngle, fYAngle, fZAngle);
1072         rnVerticalAngleDegree*=-1;
1073     }
1074     else
1075     {
1076         fXAngle = BaseGFXHelper::Rad2Deg( fXAngle );
1077         fYAngle = BaseGFXHelper::Rad2Deg( fYAngle );
1078         fZAngle = BaseGFXHelper::Rad2Deg( fZAngle );
1079 
1080         rnHorizontalAngleDegree = ::basegfx::fround(fXAngle);
1081         rnVerticalAngleDegree = ::basegfx::fround(-1.0*fYAngle);
1082         //nZRotation = ::basegfx::fround(-1.0*fZAngle);
1083     }
1084 
1085     lcl_shiftAngleToIntervalMinus180To180( rnHorizontalAngleDegree );
1086     lcl_shiftAngleToIntervalMinus180To180( rnVerticalAngleDegree );
1087 }
1088 
setRotationToDiagram(const uno::Reference<beans::XPropertySet> & xSceneProperties,sal_Int32 nHorizontalAngleDegree,sal_Int32 nVerticalYAngleDegree)1089 void ThreeDHelper::setRotationToDiagram( const uno::Reference< beans::XPropertySet >& xSceneProperties
1090             , sal_Int32 nHorizontalAngleDegree, sal_Int32 nVerticalYAngleDegree )
1091 {
1092     //todo: x and y is not equal to horz and vert in case of RightAngledAxes==false
1093     double fXAngle = BaseGFXHelper::Deg2Rad( nHorizontalAngleDegree );
1094     double fYAngle = BaseGFXHelper::Deg2Rad( -1*nVerticalYAngleDegree );
1095     double fZAngle = 0.0;
1096 
1097     if( !lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
1098         ThreeDHelper::convertElevationRotationDegToXYZAngleRad(
1099             nHorizontalAngleDegree, -1*nVerticalYAngleDegree, fXAngle, fYAngle, fZAngle );
1100 
1101     ThreeDHelper::setRotationAngleToDiagram( xSceneProperties, fXAngle, fYAngle, fZAngle );
1102 }
1103 
getCameraDistanceRange(double & rfMinimumDistance,double & rfMaximumDistance)1104 void ThreeDHelper::getCameraDistanceRange( double& rfMinimumDistance, double& rfMaximumDistance )
1105 {
1106     rfMinimumDistance = 3.0/4.0*FIXED_SIZE_FOR_3D_CHART_VOLUME;//empiric value
1107     rfMaximumDistance = 20.0*FIXED_SIZE_FOR_3D_CHART_VOLUME;//empiric value
1108 }
1109 
ensureCameraDistanceRange(double & rfCameraDistance)1110 void ThreeDHelper::ensureCameraDistanceRange( double& rfCameraDistance )
1111 {
1112     double fMin, fMax;
1113     getCameraDistanceRange( fMin, fMax );
1114     if( rfCameraDistance < fMin )
1115         rfCameraDistance = fMin;
1116     if( rfCameraDistance > fMax )
1117         rfCameraDistance = fMax;
1118 }
1119 
getCameraDistance(const Reference<beans::XPropertySet> & xSceneProperties)1120 double ThreeDHelper::getCameraDistance(
1121         const Reference< beans::XPropertySet >& xSceneProperties )
1122 {
1123     double fCameraDistance = FIXED_SIZE_FOR_3D_CHART_VOLUME;
1124 
1125     if( !xSceneProperties.is() )
1126         return fCameraDistance;
1127 
1128     try
1129     {
1130         drawing::CameraGeometry aCG( ThreeDHelper::getDefaultCameraGeometry() );
1131         xSceneProperties->getPropertyValue( C2U( "D3DCameraGeometry" ) ) >>= aCG;
1132         ::basegfx::B3DVector aVRP( BaseGFXHelper::Position3DToB3DVector( aCG.vrp ) );
1133         fCameraDistance = aVRP.getLength();
1134 
1135         ensureCameraDistanceRange( fCameraDistance );
1136     }
1137     catch( const uno::Exception & ex )
1138     {
1139         ASSERT_EXCEPTION( ex );
1140     }
1141     return fCameraDistance;
1142 }
1143 
setCameraDistance(const Reference<beans::XPropertySet> & xSceneProperties,double fCameraDistance)1144 void ThreeDHelper::setCameraDistance(
1145         const Reference< beans::XPropertySet >& xSceneProperties, double fCameraDistance )
1146 {
1147     if( !xSceneProperties.is() )
1148         return;
1149 
1150     try
1151     {
1152         if( fCameraDistance <= 0 )
1153             fCameraDistance = FIXED_SIZE_FOR_3D_CHART_VOLUME;
1154 
1155         drawing::CameraGeometry aCG( ThreeDHelper::getDefaultCameraGeometry() );
1156         xSceneProperties->getPropertyValue( C2U( "D3DCameraGeometry" ) ) >>= aCG;
1157         ::basegfx::B3DVector aVRP( BaseGFXHelper::Position3DToB3DVector( aCG.vrp ) );
1158         if( ::basegfx::fTools::equalZero( aVRP.getLength() ) )
1159             aVRP = ::basegfx::B3DVector(0,0,1);
1160         aVRP.setLength(fCameraDistance);
1161         aCG.vrp = BaseGFXHelper::B3DVectorToPosition3D( aVRP );
1162 
1163         xSceneProperties->setPropertyValue( C2U("D3DCameraGeometry"), uno::makeAny( aCG ));
1164     }
1165     catch( const uno::Exception & ex )
1166     {
1167         ASSERT_EXCEPTION( ex );
1168     }
1169 }
1170 
CameraDistanceToPerspective(double fCameraDistance)1171 double ThreeDHelper::CameraDistanceToPerspective( double fCameraDistance )
1172 {
1173     double fRet = fCameraDistance;
1174     double fMin, fMax;
1175     ThreeDHelper::getCameraDistanceRange( fMin, fMax );
1176     //fMax <-> 0; fMin <->100
1177     //a/x + b = y
1178     double a = 100.0*fMax*fMin/(fMax-fMin);
1179     double b = -a/fMax;
1180 
1181     fRet = a/fCameraDistance + b;
1182 
1183     return fRet;
1184 }
1185 
PerspectiveToCameraDistance(double fPerspective)1186 double ThreeDHelper::PerspectiveToCameraDistance( double fPerspective )
1187 {
1188     double fRet = fPerspective;
1189     double fMin, fMax;
1190     ThreeDHelper::getCameraDistanceRange( fMin, fMax );
1191     //fMax <-> 0; fMin <->100
1192     //a/x + b = y
1193     double a = 100.0*fMax*fMin/(fMax-fMin);
1194     double b = -a/fMax;
1195 
1196     fRet = a/(fPerspective - b);
1197 
1198     return fRet;
1199 }
1200 
detectScheme(const uno::Reference<XDiagram> & xDiagram)1201 ThreeDLookScheme ThreeDHelper::detectScheme( const uno::Reference< XDiagram >& xDiagram )
1202 {
1203     ThreeDLookScheme aScheme = ThreeDLookScheme_Unknown;
1204 
1205     sal_Int32 nRoundedEdges;
1206     sal_Int32 nObjectLines;
1207     ThreeDHelper::getRoundedEdgesAndObjectLines( xDiagram, nRoundedEdges, nObjectLines );
1208 
1209     //get shade mode and light settings:
1210     drawing::ShadeMode aShadeMode( drawing::ShadeMode_SMOOTH );
1211     uno::Reference< beans::XPropertySet > xDiagramProps( xDiagram, uno::UNO_QUERY );
1212     try
1213     {
1214         if( xDiagramProps.is() )
1215             xDiagramProps->getPropertyValue( C2U( "D3DSceneShadeMode" ) )>>= aShadeMode;
1216     }
1217     catch( uno::Exception & ex )
1218     {
1219         ASSERT_EXCEPTION( ex );
1220     }
1221 
1222     if( lcl_isSimpleScheme( aShadeMode, nRoundedEdges, nObjectLines, xDiagram ) )
1223     {
1224         if( lcl_isSimpleLightScheme(xDiagramProps) )
1225             aScheme = ThreeDLookScheme_Simple;
1226     }
1227     else if( lcl_isRealisticScheme( aShadeMode, nRoundedEdges, nObjectLines ) )
1228     {
1229         if( lcl_isRealisticLightScheme(xDiagramProps) )
1230             aScheme = ThreeDLookScheme_Realistic;
1231     }
1232 
1233     return aScheme;
1234 }
1235 
setScheme(const uno::Reference<XDiagram> & xDiagram,ThreeDLookScheme aScheme)1236 void ThreeDHelper::setScheme( const uno::Reference< XDiagram >& xDiagram, ThreeDLookScheme aScheme )
1237 {
1238     if( aScheme == ThreeDLookScheme_Unknown )
1239         return;
1240 
1241     drawing::ShadeMode aShadeMode;
1242     sal_Int32 nRoundedEdges;
1243     sal_Int32 nObjectLines;
1244 
1245     if( aScheme == ThreeDLookScheme_Simple )
1246         lcl_setSimpleScheme(aShadeMode,nRoundedEdges,nObjectLines,xDiagram);
1247     else
1248         lcl_setRealisticScheme(aShadeMode,nRoundedEdges,nObjectLines);
1249 
1250     try
1251     {
1252         ThreeDHelper::setRoundedEdgesAndObjectLines( xDiagram, nRoundedEdges, nObjectLines );
1253 
1254         uno::Reference< beans::XPropertySet > xProp( xDiagram, uno::UNO_QUERY );
1255         if( xProp.is() )
1256         {
1257             drawing::ShadeMode aOldShadeMode;
1258             if( ! ( (xProp->getPropertyValue( C2U( "D3DSceneShadeMode" ) )>>=aOldShadeMode) &&
1259                     aOldShadeMode == aShadeMode ))
1260             {
1261                 xProp->setPropertyValue( C2U( "D3DSceneShadeMode" ), uno::makeAny( aShadeMode ));
1262             }
1263         }
1264 
1265         lcl_setLightsForScheme( xProp, aScheme );
1266     }
1267     catch( uno::Exception & ex )
1268     {
1269         ASSERT_EXCEPTION( ex );
1270     }
1271 
1272 }
1273 
set3DSettingsToDefault(const uno::Reference<beans::XPropertySet> & xSceneProperties)1274 void ThreeDHelper::set3DSettingsToDefault( const uno::Reference< beans::XPropertySet >& xSceneProperties )
1275 {
1276     Reference< beans::XPropertyState > xState( xSceneProperties, uno::UNO_QUERY );
1277     if(xState.is())
1278     {
1279         xState->setPropertyToDefault( C2U("D3DSceneDistance"));
1280         xState->setPropertyToDefault( C2U("D3DSceneFocalLength"));
1281     }
1282     ThreeDHelper::setDefaultRotation( xSceneProperties );
1283     ThreeDHelper::setDefaultIllumination( xSceneProperties );
1284 }
1285 
setDefaultRotation(const uno::Reference<beans::XPropertySet> & xSceneProperties,bool bPieOrDonut)1286 void ThreeDHelper::setDefaultRotation( const uno::Reference< beans::XPropertySet >& xSceneProperties, bool bPieOrDonut )
1287 {
1288     if( !xSceneProperties.is() )
1289         return;
1290 
1291     drawing::CameraGeometry aCameraGeo( ThreeDHelper::getDefaultCameraGeometry( bPieOrDonut ) );
1292     xSceneProperties->setPropertyValue( C2U("D3DCameraGeometry"), uno::makeAny( aCameraGeo ));
1293 
1294     ::basegfx::B3DHomMatrix aSceneRotation;
1295     if( bPieOrDonut )
1296         aSceneRotation.rotate( -F_PI/3.0, 0, 0 );
1297     xSceneProperties->setPropertyValue( C2U("D3DTransformMatrix"),
1298         uno::makeAny( BaseGFXHelper::B3DHomMatrixToHomogenMatrix( aSceneRotation )));
1299 }
1300 
setDefaultRotation(const uno::Reference<beans::XPropertySet> & xSceneProperties)1301 void ThreeDHelper::setDefaultRotation( const uno::Reference< beans::XPropertySet >& xSceneProperties )
1302 {
1303     bool bPieOrDonut( DiagramHelper::isPieOrDonutChart( uno::Reference< XDiagram >(xSceneProperties, uno::UNO_QUERY) ) );
1304     ThreeDHelper::setDefaultRotation( xSceneProperties, bPieOrDonut );
1305 }
1306 
setDefaultIllumination(const uno::Reference<beans::XPropertySet> & xSceneProperties)1307 void ThreeDHelper::setDefaultIllumination( const uno::Reference< beans::XPropertySet >& xSceneProperties )
1308 {
1309     if( !xSceneProperties.is() )
1310         return;
1311 
1312     drawing::ShadeMode aShadeMode( drawing::ShadeMode_SMOOTH );
1313     try
1314     {
1315         xSceneProperties->getPropertyValue( C2U( "D3DSceneShadeMode" ) )>>= aShadeMode;
1316         xSceneProperties->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTON_1 ), uno::makeAny( sal_False ) );
1317         xSceneProperties->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTON_3 ), uno::makeAny( sal_False ) );
1318         xSceneProperties->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTON_4 ), uno::makeAny( sal_False ) );
1319         xSceneProperties->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTON_5 ), uno::makeAny( sal_False ) );
1320         xSceneProperties->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTON_6 ), uno::makeAny( sal_False ) );
1321         xSceneProperties->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTON_7 ), uno::makeAny( sal_False ) );
1322         xSceneProperties->setPropertyValue( C2U( UNO_NAME_3D_SCENE_LIGHTON_8 ), uno::makeAny( sal_False ) );
1323     }
1324     catch( uno::Exception & ex )
1325     {
1326         ASSERT_EXCEPTION( ex );
1327     }
1328 
1329     ThreeDLookScheme aScheme = (drawing::ShadeMode_FLAT==aShadeMode) ? ThreeDLookScheme_Simple : ThreeDLookScheme_Realistic;
1330     lcl_setLightsForScheme( xSceneProperties, aScheme );
1331 }
1332 
getRoundedEdgesAndObjectLines(const uno::Reference<XDiagram> & xDiagram,sal_Int32 & rnRoundedEdges,sal_Int32 & rnObjectLines)1333 void ThreeDHelper::getRoundedEdgesAndObjectLines(
1334             const uno::Reference< XDiagram > & xDiagram
1335             , sal_Int32& rnRoundedEdges, sal_Int32& rnObjectLines )
1336 {
1337     rnRoundedEdges = -1;
1338     rnObjectLines = -1;
1339     try
1340     {
1341         bool bDifferentRoundedEdges = false;
1342         bool bDifferentObjectLines = false;
1343 
1344         drawing::LineStyle aLineStyle( drawing::LineStyle_SOLID );
1345 
1346         ::std::vector< uno::Reference< XDataSeries > > aSeriesList(
1347             DiagramHelper::getDataSeriesFromDiagram( xDiagram ) );
1348         sal_Int32 nSeriesCount = static_cast<sal_Int32>( aSeriesList.size() );
1349 
1350         rtl::OUString aPercentDiagonalPropertyName( C2U( "PercentDiagonal" ) );
1351         rtl::OUString aBorderStylePropertyName( C2U( "BorderStyle" ) );
1352 
1353         for( sal_Int32 nS = 0; nS < nSeriesCount; ++nS )
1354         {
1355 		    uno::Reference< XDataSeries > xSeries( aSeriesList[nS] );
1356             uno::Reference< beans::XPropertySet > xProp( xSeries, uno::UNO_QUERY );
1357             if(!nS)
1358             {
1359                 rnRoundedEdges = 0;
1360                 try
1361                 {
1362                     sal_Int16 nPercentDiagonal = 0;
1363 
1364                     xProp->getPropertyValue( aPercentDiagonalPropertyName ) >>= nPercentDiagonal;
1365                     rnRoundedEdges = static_cast< sal_Int32 >( nPercentDiagonal );
1366 
1367                     if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
1368                         , aPercentDiagonalPropertyName, uno::makeAny(nPercentDiagonal) ) )
1369                         bDifferentRoundedEdges = true;
1370                 }
1371                 catch( uno::Exception& e )
1372 		        {
1373                     ASSERT_EXCEPTION( e );
1374                     bDifferentRoundedEdges = true;
1375                 }
1376                 try
1377                 {
1378                     xProp->getPropertyValue( aBorderStylePropertyName ) >>= aLineStyle;
1379 
1380                     if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
1381                         , aBorderStylePropertyName, uno::makeAny(aLineStyle) ) )
1382                         bDifferentObjectLines = true;
1383                 }
1384                 catch( uno::Exception& e )
1385 		        {
1386                     ASSERT_EXCEPTION( e );
1387                     bDifferentObjectLines = true;
1388                 }
1389             }
1390             else
1391             {
1392                 if( !bDifferentRoundedEdges )
1393                 {
1394                     sal_Int16 nPercentDiagonal = 0;
1395                     xProp->getPropertyValue( aPercentDiagonalPropertyName ) >>= nPercentDiagonal;
1396                     sal_Int32 nCurrentRoundedEdges = static_cast< sal_Int32 >( nPercentDiagonal );
1397                     if(nCurrentRoundedEdges!=rnRoundedEdges
1398                         || DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
1399                             , aPercentDiagonalPropertyName, uno::makeAny( static_cast< sal_Int16 >(rnRoundedEdges) ) ) )
1400                     {
1401                         bDifferentRoundedEdges = true;
1402                         nCurrentRoundedEdges = -1;
1403                     }
1404                 }
1405 
1406                 if( !bDifferentObjectLines )
1407                 {
1408                     drawing::LineStyle aCurrentLineStyle;
1409                     xProp->getPropertyValue( aBorderStylePropertyName ) >>= aCurrentLineStyle;
1410                     if(aCurrentLineStyle!=aLineStyle
1411                         || DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
1412                             , aBorderStylePropertyName, uno::makeAny(aLineStyle) ) )
1413                         bDifferentObjectLines = true;
1414                 }
1415             }
1416             if( bDifferentRoundedEdges && bDifferentObjectLines )
1417                 break;
1418         }
1419 
1420         //set rnObjectLines
1421         rnObjectLines = 0;
1422         if( bDifferentObjectLines )
1423             rnObjectLines = -1;
1424         else if( aLineStyle == drawing::LineStyle_SOLID )
1425             rnObjectLines = 1;
1426     }
1427     catch( uno::Exception& e )
1428 	{
1429         ASSERT_EXCEPTION( e );
1430     }
1431 }
setRoundedEdgesAndObjectLines(const uno::Reference<XDiagram> & xDiagram,sal_Int32 nRoundedEdges,sal_Int32 nObjectLines)1432 void ThreeDHelper::setRoundedEdgesAndObjectLines(
1433             const uno::Reference< XDiagram > & xDiagram
1434             , sal_Int32 nRoundedEdges, sal_Int32 nObjectLines )
1435 {
1436     if( (nRoundedEdges<0||nRoundedEdges>100) && nObjectLines!=0 && nObjectLines!=1 )
1437         return;
1438 
1439     drawing::LineStyle aLineStyle( drawing::LineStyle_NONE );
1440     if(nObjectLines==1)
1441         aLineStyle = drawing::LineStyle_SOLID;
1442 
1443     uno::Any aALineStyle( uno::makeAny(aLineStyle));
1444     uno::Any aARoundedEdges( uno::makeAny( static_cast< sal_Int16 >( nRoundedEdges )));
1445 
1446     ::std::vector< uno::Reference< XDataSeries > > aSeriesList(
1447         DiagramHelper::getDataSeriesFromDiagram( xDiagram ) );
1448     sal_Int32 nSeriesCount = static_cast<sal_Int32>( aSeriesList.size() );
1449     for( sal_Int32 nS = 0; nS < nSeriesCount; ++nS )
1450     {
1451 		uno::Reference< XDataSeries > xSeries( aSeriesList[nS] );
1452 
1453         if( nRoundedEdges>=0 && nRoundedEdges<=100 )
1454             DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries, C2U( "PercentDiagonal" ), aARoundedEdges );
1455 
1456         if( nObjectLines==0 || nObjectLines==1 )
1457             DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries, C2U( "BorderStyle" ), aALineStyle );
1458     }
1459 }
1460 
getAutomaticCuboidPlanePositionForStandardLeftWall(const Reference<beans::XPropertySet> & xSceneProperties)1461 CuboidPlanePosition ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( const Reference< beans::XPropertySet >& xSceneProperties )
1462 {
1463     CuboidPlanePosition eRet(CuboidPlanePosition_Left);
1464 
1465     double fXAngleRad=0.0; double fYAngleRad=0.0; double fZAngleRad=0.0;
1466     ThreeDHelper::getRotationAngleFromDiagram( xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
1467     if( lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
1468     {
1469         ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad, fYAngleRad );
1470         fZAngleRad=0.0;
1471     }
1472     if( sin(fYAngleRad)>0.0 )
1473         eRet = CuboidPlanePosition_Right;
1474     return eRet;
1475 }
1476 
getAutomaticCuboidPlanePositionForStandardBackWall(const Reference<beans::XPropertySet> & xSceneProperties)1477 CuboidPlanePosition ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( const Reference< beans::XPropertySet >& xSceneProperties )
1478 {
1479     CuboidPlanePosition eRet(CuboidPlanePosition_Back);
1480 
1481     double fXAngleRad=0.0; double fYAngleRad=0.0; double fZAngleRad=0.0;
1482     ThreeDHelper::getRotationAngleFromDiagram( xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
1483     if( lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
1484     {
1485         ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad, fYAngleRad );
1486         fZAngleRad=0.0;
1487     }
1488     if( cos(fXAngleRad)*cos(fYAngleRad)<0.0 )
1489         eRet = CuboidPlanePosition_Front;
1490     return eRet;
1491 }
1492 
getAutomaticCuboidPlanePositionForStandardBottom(const Reference<beans::XPropertySet> & xSceneProperties)1493 CuboidPlanePosition ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( const Reference< beans::XPropertySet >& xSceneProperties )
1494 {
1495     CuboidPlanePosition eRet(CuboidPlanePosition_Bottom);
1496 
1497     double fXAngleRad=0.0; double fYAngleRad=0.0; double fZAngleRad=0.0;
1498     ThreeDHelper::getRotationAngleFromDiagram( xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
1499     if( lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
1500     {
1501         ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad, fYAngleRad );
1502         fZAngleRad=0.0;
1503     }
1504     if( sin(fXAngleRad)*cos(fYAngleRad)<0.0 )
1505         eRet = CuboidPlanePosition_Top;
1506     return eRet;
1507 }
1508 
1509 //.............................................................................
1510 } //namespace chart
1511 //.............................................................................
1512