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