1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <ThreeDHelper.hxx>
21 #include <Diagram.hxx>
22 #include <DiagramHelper.hxx>
23 #include <ChartTypeHelper.hxx>
24 #include <ChartType.hxx>
25 #include <BaseGFXHelper.hxx>
26 #include <DataSeries.hxx>
27 #include <DataSeriesHelper.hxx>
28 #include <defines.hxx>
30 #include <editeng/unoprnms.hxx>
31 #include <com/sun/star/drawing/LineStyle.hpp>
32 #include <com/sun/star/drawing/ShadeMode.hpp>
33 #include <comphelper/diagnose_ex.hxx>
34 #include <tools/helpers.hxx>
35 #include <rtl/math.hxx>
39 using namespace ::com::sun::star
;
40 using namespace ::com::sun::star::chart2
;
42 using ::com::sun::star::uno::Reference
;
43 using ::rtl::math::cos
;
44 using ::rtl::math::sin
;
45 using ::rtl::math::tan
;
50 bool lcl_isRightAngledAxesSetAndSupported( const rtl::Reference
< Diagram
>& xDiagram
)
54 bool bRightAngledAxes
= false;
55 xDiagram
->getPropertyValue( "RightAngledAxes") >>= bRightAngledAxes
;
58 if( ChartTypeHelper::isSupportingRightAngledAxes(
59 xDiagram
->getChartTypeByIndex( 0 ) ) )
68 } //end anonymous namespace
70 drawing::CameraGeometry
ThreeDHelper::getDefaultCameraGeometry( bool bPie
)
72 // ViewReferencePoint (Point on the View plane)
73 drawing::Position3D
vrp(17634.6218373783, 10271.4823817647, 24594.8639082739);
74 // ViewPlaneNormal (Normal to the View Plane)
75 drawing::Direction3D
vpn(0.416199821709347, 0.173649045905254, 0.892537795986984);
76 // ViewUpVector (determines the v-axis direction on the view plane as
77 // projection of VUP parallel to VPN onto th view pane)
78 drawing::Direction3D
vup(-0.0733876362771618, 0.984807599917971, -0.157379306090273);
82 vrp
= drawing::Position3D( 0.0, 0.0, 87591.2408759124 );//--> 5 percent perspective
83 vpn
= drawing::Direction3D( 0.0, 0.0, 1.0 );
84 vup
= drawing::Direction3D( 0.0, 1.0, 0.0 );
87 return drawing::CameraGeometry( vrp
, vpn
, vup
);
92 void lcl_ensureIntervalMinus1To1( double& rSinOrCos
)
96 else if (rSinOrCos
> 1.0)
100 bool lcl_isSinZero( double fAngleRad
)
102 return ::basegfx::fTools::equalZero( sin(fAngleRad
), 0.0000001 );
104 bool lcl_isCosZero( double fAngleRad
)
106 return ::basegfx::fTools::equalZero( cos(fAngleRad
), 0.0000001 );
111 void ThreeDHelper::convertElevationRotationDegToXYZAngleRad(
112 sal_Int32 nElevationDeg
, sal_Int32 nRotationDeg
,
113 double& rfXAngleRad
, double& rfYAngleRad
, double& rfZAngleRad
)
115 // for a description of the algorithm see issue 72994
116 //https://bz.apache.org/ooo/show_bug.cgi?id=72994
117 //https://bz.apache.org/ooo/attachment.cgi?id=50608
119 nElevationDeg
= NormAngle360(nElevationDeg
);
120 nRotationDeg
= NormAngle360(nRotationDeg
);
122 double& x
= rfXAngleRad
;
123 double& y
= rfYAngleRad
;
124 double& z
= rfZAngleRad
;
126 double E
= basegfx::deg2rad(nElevationDeg
); //elevation in Rad
127 double R
= basegfx::deg2rad(nRotationDeg
); //rotation in Rad
129 if( (nRotationDeg
== 0 || nRotationDeg
== 180 )
130 && ( nElevationDeg
== 90 || nElevationDeg
== 270 ) )
135 double f23
= cos(R
)*sin(E
);
142 else if( ( nRotationDeg
== 90 || nRotationDeg
== 270 )
143 && ( nElevationDeg
== 90 || nElevationDeg
== 270 ) )
152 if( (sin(R
)*sin(E
))>0 )
157 else if( (nRotationDeg
== 0 || nRotationDeg
== 180 )
158 && ( nElevationDeg
== 0 || nElevationDeg
== 180 ) )
165 else if( ( nRotationDeg
== 90 || nRotationDeg
== 270 )
166 && ( nElevationDeg
== 0 || nElevationDeg
== 180 ) )
171 if( (sin(R
)/cos(E
))>0 )
181 else if ( nElevationDeg
== 0 || nElevationDeg
== 180 )
187 //use element 13 for sign
188 if((cos(x
)*sin(y
)*sin(R
))<0.0)
191 else if ( nElevationDeg
== 90 || nElevationDeg
== 270 )
194 //element 12 + 22 --> y=0 or M_PI and x=+-M_PI/2
196 z
= atan(sin(R
)/(cos(R
)*sin(E
)));
197 //use element 13 for sign for x
198 if( (sin(R
)*sin(z
))>0.0 )
202 //use element 21 for y
203 if( (sin(R
)*sin(E
)*sin(z
))>0.0)
208 else if ( nRotationDeg
== 0 || nRotationDeg
== 180 )
214 double f23
= cos(R
)*sin(E
);
215 if( (f23
* sin(x
)) < 0.0 )
218 else if (nRotationDeg
== 90 || nRotationDeg
== 270)
227 x
*= -1.0; //different signs for x and z
230 double cy
= sR
*sin(E
)/sin(z
);
231 lcl_ensureIntervalMinus1To1(cy
);
234 //use element 22 for sign:
235 if( (sin(x
)*sin(y
)*sin(z
)*cos(E
))<0.0)
240 z
= atan(tan(R
) * sin(E
));
243 OSL_FAIL("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
246 double cy
= cos(R
)/cos(z
);
247 lcl_ensureIntervalMinus1To1(cy
);
251 double fDenominator
= cos(z
)*(1.0-pow(sin(y
),2));
252 if(fDenominator
==0.0)
254 OSL_FAIL("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
257 double sx
= cos(R
)*sin(E
)/fDenominator
;
258 lcl_ensureIntervalMinus1To1(sx
);
261 //use element 13 for sign:
262 double f13a
= cos(x
)*cos(z
)*sin(y
);
263 double f13b
= sin(R
)-sx
*sin(z
);
264 if( (f13b
*f13a
)<0.0 )
267 //use element 22 for further investigations:
270 double f22a
= cos(x
)*cos(z
);
271 double f22b
= cos(E
)-(sx
*sin(y
)*sin(z
));
272 if( (f22a
*f22b
)<0.0 )
280 //change nothing or both
281 //use element 22 for further investigations:
282 double f22a
= cos(x
)*cos(z
);
283 double f22b
= cos(E
)-(sx
*sin(y
)*sin(z
));
284 if( (f22a
*f22b
)<0.0 )
293 void ThreeDHelper::convertXYZAngleRadToElevationRotationDeg(
294 sal_Int32
& rnElevationDeg
, sal_Int32
& rnRotationDeg
,
295 double fXRad
, double fYRad
, double fZRad
)
297 // for a description of the algorithm see issue 72994
298 //https://bz.apache.org/ooo/show_bug.cgi?id=72994
299 //https://bz.apache.org/ooo/attachment.cgi?id=50608
301 double R
= 0.0; //Rotation in Rad
302 double E
= 0.0; //Elevation in Rad
308 double f11
= cos(y
)*cos(z
);
310 if( lcl_isSinZero(y
) )
314 if( lcl_isCosZero(x
) )
316 //siny == 0 && cosx == 0
318 if( lcl_isSinZero(z
) )
320 //siny == 0 && cosx == 0 && sinz == 0
321 //example: x=+-90 y=0oder180 z=0(oder180)
330 double f23
= cos(z
)*sin(x
) / cos(R
);
336 else if( lcl_isCosZero(z
) )
338 //siny == 0 && cosx == 0 && cosz == 0
339 //example: x=+-90 y=0oder180 z=+-90
341 double f13
= sin(x
)*sin(z
);
349 double f21
= cos(y
)*sin(z
) / sin(R
);
357 //siny == 0 && cosx == 0 && cosz != 0 && sinz != 0
359 double f13
= sin(x
)*sin(z
);
366 double f23
= cos(z
)*sin(x
);
373 else if( lcl_isSinZero(x
) )
382 double f22
= cos(x
)*cos(z
);
388 else if( lcl_isSinZero(z
) )
390 //sinY==0 sinZ==0 sinx!=0 cosx!=0
398 double f22
= cos(x
)*cos(z
);
399 double f23
= cos(z
)*sin(x
);
400 E
= atan( f23
/(f22
*cos(R
)) );
404 else if( lcl_isCosZero(z
) )
406 //sinY == 0 && cosZ == 0 && cosx != 0 && sinx != 0
407 double f13
= sin(x
)*sin(z
);
415 double f21
= cos(y
)*sin(z
);
423 //sinY == 0 && all other !=0
424 double f13
= sin(x
)*sin(z
);
426 if( (f11
*cos(R
))<0.0 )
429 double f22
= cos(x
)*cos(z
);
430 if( !lcl_isCosZero(R
) )
431 E
= atan( cos(z
)*sin(x
) /( f22
*cos(R
) ) );
433 E
= atan( cos(y
)*sin(z
) /( f22
*sin(R
) ) );
438 else if( lcl_isCosZero(y
) )
442 double f13
= sin(x
)*sin(z
)+cos(x
)*cos(z
)*sin(y
);
448 double f22
= cos(x
)*cos(z
)+sin(x
)*sin(y
)*sin(z
);
454 else if( lcl_isSinZero(x
) )
456 //cosY!=0 sinY!=0 sinX=0
457 if( lcl_isSinZero(z
) )
459 //cosY!=0 sinY!=0 sinX=0 sinZ=0
460 double f13
= cos(x
)*cos(z
)*sin(y
);
466 double f22
= cos(x
)*cos(z
);
472 else if( lcl_isCosZero(z
) )
474 //cosY!=0 sinY!=0 sinX=0 cosZ=0
478 double f23
= -1.0*cos(x
)*sin(y
)*sin(z
);
479 if( (f23
*cos(R
)*sin(E
))<0.0 )
487 //cosY!=0 sinY!=0 sinX=0 sinZ!=0 cosZ!=0
488 double f13
= cos(x
)*cos(z
)*sin(y
);
494 double f21
= cos(y
)*sin(z
);
495 double f22
= cos(x
)*cos(z
);
496 E
= atan(f21
/(f22
*sin(R
)) );
498 if( (f22
*cos(E
))<0.0 )
502 else if( lcl_isCosZero(x
) )
504 //cosY!=0 sinY!=0 cosX=0
506 if( lcl_isSinZero(z
) )
508 //cosY!=0 sinY!=0 cosX=0 sinZ=0
509 R
=0;//13 -> R=0 or M_PI
512 E
=M_PI_2
;//22 -> E=+-M_PI/2
513 //use element 11 and 23 for sign
514 double f23
= cos(z
)*sin(x
);
515 if( (f11
*f23
*sin(E
))<0.0 )
518 else if( lcl_isCosZero(z
) )
520 //cosY!=0 sinY!=0 cosX=0 cosZ=0
522 if( (sin(x
)*sin(z
))>0.0 )
527 E
=acos( sin(x
)*sin(y
)*sin(z
));
528 //use element 21 for sign:
529 if( (cos(y
)*sin(z
)*sin(R
)*sin(E
))<0.0 )
534 //cosY!=0 sinY!=0 cosX=0 sinZ!=0 cosZ!=0
536 R
= atan( sin(x
)*sin(z
)/(cos(y
)*cos(z
)) );
538 if( (sin(x
)*sin(z
))<0.0 )
541 E
= acos(sin(x
)*sin(y
)*sin(z
) );
543 if( (cos(y
)*sin(z
)*sin(R
)*sin(E
))<0.0 )
547 else if( lcl_isSinZero(z
) )
549 //cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ=0
552 //use element 13 for sign
553 if( (cos(x
)*cos(z
)*sin(y
)*sin(R
))<0.0 )
556 E
= acos( cos(x
)*cos(z
) );
557 //use element 23 for sign
558 if( (cos(z
)*sin(x
)*cos(R
)*sin(E
))<0.0 )
561 else if( lcl_isCosZero(z
) )
563 //cosY!=0 sinY!=0 sinX!=0 cosX!=0 cosZ=0
565 R
=atan(-cos(y
)/(cos(x
)*sin(y
)));
566 //use element 13 for 'sign'
567 if( (sin(x
)*sin(z
)*sin(R
))<0.0 )
570 E
=atan( cos(y
)*sin(z
)/(sin(R
)*sin(x
)*sin(y
)*sin(z
)) );
571 //use element 23 for 'sign'
572 if( (-cos(x
)*sin(y
)*sin(z
)*cos(R
)*sin(E
))<0.0 )
577 //cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ!=0 cosZ!=0
579 double f13
= sin(x
)*sin(z
)+cos(x
)*cos(z
)*sin(y
);
580 R
= atan( f13
/ f11
);
583 double f22
= cos(x
)*cos(z
)+sin(x
)*sin(y
)*sin(z
);
584 double f23
= cos(x
)*sin(y
)*sin(z
)-cos(z
)*sin(x
);
586 E
= atan( -1.0*f23
/(f22
*cos(R
)) );
591 rnElevationDeg
= basegfx::fround(basegfx::rad2deg(E
));
592 rnRotationDeg
= basegfx::fround(basegfx::rad2deg(R
));
595 double ThreeDHelper::getValueClippedToRange( double fAngle
, const double& fPositivLimit
)
597 if( fAngle
<-1*fPositivLimit
)
598 fAngle
=-1*fPositivLimit
;
599 else if( fAngle
>fPositivLimit
)
600 fAngle
=fPositivLimit
;
604 void ThreeDHelper::adaptRadAnglesForRightAngledAxes( double& rfXAngleRad
, double& rfYAngleRad
)
606 rfXAngleRad
= ThreeDHelper::getValueClippedToRange(rfXAngleRad
, basegfx::deg2rad(ThreeDHelper::getXDegreeAngleLimitForRightAngledAxes()) );
607 rfYAngleRad
= ThreeDHelper::getValueClippedToRange(rfYAngleRad
, basegfx::deg2rad(ThreeDHelper::getYDegreeAngleLimitForRightAngledAxes()) );
611 void ThreeDHelper::getCameraDistanceRange( double& rfMinimumDistance
, double& rfMaximumDistance
)
613 rfMinimumDistance
= 3.0/4.0*FIXED_SIZE_FOR_3D_CHART_VOLUME
;//empiric value
614 rfMaximumDistance
= 20.0*FIXED_SIZE_FOR_3D_CHART_VOLUME
;//empiric value
617 void ThreeDHelper::ensureCameraDistanceRange( double& rfCameraDistance
)
620 getCameraDistanceRange( fMin
, fMax
);
621 if( rfCameraDistance
< fMin
)
622 rfCameraDistance
= fMin
;
623 if( rfCameraDistance
> fMax
)
624 rfCameraDistance
= fMax
;
627 double ThreeDHelper::CameraDistanceToPerspective( double fCameraDistance
)
630 ThreeDHelper::getCameraDistanceRange( fMin
, fMax
);
631 //fMax <-> 0; fMin <->100
633 double a
= 100.0*fMax
*fMin
/(fMax
-fMin
);
636 double fRet
= a
/fCameraDistance
+ b
;
641 double ThreeDHelper::PerspectiveToCameraDistance( double fPerspective
)
644 ThreeDHelper::getCameraDistanceRange( fMin
, fMax
);
645 //fMax <-> 0; fMin <->100
647 double a
= 100.0*fMax
*fMin
/(fMax
-fMin
);
650 double fRet
= a
/(fPerspective
- b
);
655 void ThreeDHelper::getRoundedEdgesAndObjectLines(
656 const rtl::Reference
< Diagram
> & xDiagram
657 , sal_Int32
& rnRoundedEdges
, sal_Int32
& rnObjectLines
)
663 bool bDifferentRoundedEdges
= false;
664 bool bDifferentObjectLines
= false;
666 drawing::LineStyle
aLineStyle( drawing::LineStyle_SOLID
);
668 std::vector
< rtl::Reference
< DataSeries
> > aSeriesList
=
669 xDiagram
->getDataSeries();
670 sal_Int32 nSeriesCount
= static_cast<sal_Int32
>( aSeriesList
.size() );
672 OUString
aPercentDiagonalPropertyName( "PercentDiagonal" );
673 OUString
aBorderStylePropertyName( "BorderStyle" );
675 for( sal_Int32 nS
= 0; nS
< nSeriesCount
; ++nS
)
677 rtl::Reference
< DataSeries
> xSeries( aSeriesList
[nS
] );
683 sal_Int16 nPercentDiagonal
= 0;
685 xSeries
->getPropertyValue( aPercentDiagonalPropertyName
) >>= nPercentDiagonal
;
686 rnRoundedEdges
= static_cast< sal_Int32
>( nPercentDiagonal
);
688 if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
689 , aPercentDiagonalPropertyName
, uno::Any(nPercentDiagonal
) ) )
690 bDifferentRoundedEdges
= true;
692 catch( const uno::Exception
& )
694 TOOLS_WARN_EXCEPTION("chart2", "" );
695 bDifferentRoundedEdges
= true;
699 xSeries
->getPropertyValue( aBorderStylePropertyName
) >>= aLineStyle
;
701 if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
702 , aBorderStylePropertyName
, uno::Any(aLineStyle
) ) )
703 bDifferentObjectLines
= true;
705 catch( const uno::Exception
& )
707 TOOLS_WARN_EXCEPTION("chart2", "" );
708 bDifferentObjectLines
= true;
713 if( !bDifferentRoundedEdges
)
715 sal_Int16 nPercentDiagonal
= 0;
716 xSeries
->getPropertyValue( aPercentDiagonalPropertyName
) >>= nPercentDiagonal
;
717 sal_Int32 nCurrentRoundedEdges
= static_cast< sal_Int32
>( nPercentDiagonal
);
718 if(nCurrentRoundedEdges
!=rnRoundedEdges
719 || DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
720 , aPercentDiagonalPropertyName
, uno::Any( static_cast< sal_Int16
>(rnRoundedEdges
) ) ) )
722 bDifferentRoundedEdges
= true;
726 if( !bDifferentObjectLines
)
728 drawing::LineStyle aCurrentLineStyle
;
729 xSeries
->getPropertyValue( aBorderStylePropertyName
) >>= aCurrentLineStyle
;
730 if(aCurrentLineStyle
!=aLineStyle
731 || DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
732 , aBorderStylePropertyName
, uno::Any(aLineStyle
) ) )
733 bDifferentObjectLines
= true;
736 if( bDifferentRoundedEdges
&& bDifferentObjectLines
)
742 if( bDifferentObjectLines
)
744 else if( aLineStyle
== drawing::LineStyle_SOLID
)
747 catch( const uno::Exception
& )
749 TOOLS_WARN_EXCEPTION("chart2", "" );
753 void ThreeDHelper::setRoundedEdgesAndObjectLines(
754 const rtl::Reference
< Diagram
> & xDiagram
755 , sal_Int32 nRoundedEdges
, sal_Int32 nObjectLines
)
757 if( (nRoundedEdges
<0||nRoundedEdges
>100) && nObjectLines
!=0 && nObjectLines
!=1 )
760 drawing::LineStyle
aLineStyle( drawing::LineStyle_NONE
);
762 aLineStyle
= drawing::LineStyle_SOLID
;
764 uno::Any
aALineStyle( aLineStyle
);
765 uno::Any
aARoundedEdges( static_cast< sal_Int16
>( nRoundedEdges
));
767 std::vector
< rtl::Reference
< DataSeries
> > aSeriesList
=
768 xDiagram
->getDataSeries();
769 for( auto const& xSeries
: aSeriesList
)
771 if( nRoundedEdges
>=0 && nRoundedEdges
<=100 )
772 DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries
, "PercentDiagonal", aARoundedEdges
);
774 if( nObjectLines
==0 || nObjectLines
==1 )
775 DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries
, "BorderStyle", aALineStyle
);
779 CuboidPlanePosition
ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( const rtl::Reference
< ::chart::Diagram
>& xDiagram
)
781 CuboidPlanePosition
eRet(CuboidPlanePosition_Left
);
783 double fXAngleRad
=0.0; double fYAngleRad
=0.0; double fZAngleRad
=0.0;
784 xDiagram
->getRotationAngle( fXAngleRad
, fYAngleRad
, fZAngleRad
);
785 if( lcl_isRightAngledAxesSetAndSupported( xDiagram
) )
787 ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad
, fYAngleRad
);
789 if( sin(fYAngleRad
)>0.0 )
790 eRet
= CuboidPlanePosition_Right
;
794 CuboidPlanePosition
ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( const rtl::Reference
< Diagram
>& xDiagram
)
796 CuboidPlanePosition
eRet(CuboidPlanePosition_Back
);
798 double fXAngleRad
=0.0; double fYAngleRad
=0.0; double fZAngleRad
=0.0;
799 xDiagram
->getRotationAngle( fXAngleRad
, fYAngleRad
, fZAngleRad
);
800 if( lcl_isRightAngledAxesSetAndSupported( xDiagram
) )
802 ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad
, fYAngleRad
);
804 if( cos(fXAngleRad
)*cos(fYAngleRad
)<0.0 )
805 eRet
= CuboidPlanePosition_Front
;
809 CuboidPlanePosition
ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( const rtl::Reference
< Diagram
>& xDiagram
)
811 CuboidPlanePosition
eRet(CuboidPlanePosition_Bottom
);
813 double fXAngleRad
=0.0; double fYAngleRad
=0.0; double fZAngleRad
=0.0;
814 xDiagram
->getRotationAngle( fXAngleRad
, fYAngleRad
, fZAngleRad
);
815 if( lcl_isRightAngledAxesSetAndSupported( xDiagram
) )
817 ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad
, fYAngleRad
);
819 if( sin(fXAngleRad
)*cos(fYAngleRad
)<0.0 )
820 eRet
= CuboidPlanePosition_Top
;
826 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */