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 <basegfx/numeric/ftools.hxx>
21 #include <ThreeDHelper.hxx>
22 #include <Diagram.hxx>
23 #include <ChartTypeHelper.hxx>
24 #include <DataSeries.hxx>
25 #include <DataSeriesHelper.hxx>
26 #include <defines.hxx>
28 #include <com/sun/star/drawing/LineStyle.hpp>
29 #include <comphelper/diagnose_ex.hxx>
30 #include <tools/helpers.hxx>
31 #include <rtl/math.hxx>
35 using namespace ::com::sun::star
;
36 using namespace ::com::sun::star::chart2
;
38 using ::com::sun::star::uno::Reference
;
39 using ::rtl::math::cos
;
40 using ::rtl::math::sin
;
41 using ::rtl::math::tan
;
46 bool lcl_isRightAngledAxesSetAndSupported( const rtl::Reference
< Diagram
>& xDiagram
)
50 bool bRightAngledAxes
= false;
51 xDiagram
->getPropertyValue( u
"RightAngledAxes"_ustr
) >>= bRightAngledAxes
;
54 if( ChartTypeHelper::isSupportingRightAngledAxes(
55 xDiagram
->getChartTypeByIndex( 0 ) ) )
64 } //end anonymous namespace
66 drawing::CameraGeometry
ThreeDHelper::getDefaultCameraGeometry( bool bPie
)
68 // ViewReferencePoint (Point on the View plane)
69 drawing::Position3D
vrp(17634.6218373783, 10271.4823817647, 24594.8639082739);
70 // ViewPlaneNormal (Normal to the View Plane)
71 drawing::Direction3D
vpn(0.416199821709347, 0.173649045905254, 0.892537795986984);
72 // ViewUpVector (determines the v-axis direction on the view plane as
73 // projection of VUP parallel to VPN onto th view pane)
74 drawing::Direction3D
vup(-0.0733876362771618, 0.984807599917971, -0.157379306090273);
78 vrp
= drawing::Position3D( 0.0, 0.0, 87591.2408759124 );//--> 5 percent perspective
79 vpn
= drawing::Direction3D( 0.0, 0.0, 1.0 );
80 vup
= drawing::Direction3D( 0.0, 1.0, 0.0 );
83 return drawing::CameraGeometry( vrp
, vpn
, vup
);
88 void lcl_ensureIntervalMinus1To1( double& rSinOrCos
)
92 else if (rSinOrCos
> 1.0)
96 bool lcl_isSinZero( double fAngleRad
)
98 return ::basegfx::fTools::equalZero( sin(fAngleRad
), 0.0000001 );
100 bool lcl_isCosZero( double fAngleRad
)
102 return ::basegfx::fTools::equalZero( cos(fAngleRad
), 0.0000001 );
107 void ThreeDHelper::convertElevationRotationDegToXYZAngleRad(
108 sal_Int32 nElevationDeg
, sal_Int32 nRotationDeg
,
109 double& rfXAngleRad
, double& rfYAngleRad
, double& rfZAngleRad
)
111 // for a description of the algorithm see issue 72994
112 //https://bz.apache.org/ooo/show_bug.cgi?id=72994
113 //https://bz.apache.org/ooo/attachment.cgi?id=50608
115 nElevationDeg
= NormAngle360(nElevationDeg
);
116 nRotationDeg
= NormAngle360(nRotationDeg
);
118 double& x
= rfXAngleRad
;
119 double& y
= rfYAngleRad
;
120 double& z
= rfZAngleRad
;
122 double E
= basegfx::deg2rad(nElevationDeg
); //elevation in Rad
123 double R
= basegfx::deg2rad(nRotationDeg
); //rotation in Rad
125 if( (nRotationDeg
== 0 || nRotationDeg
== 180 )
126 && ( nElevationDeg
== 90 || nElevationDeg
== 270 ) )
131 double f23
= cos(R
)*sin(E
);
138 else if( ( nRotationDeg
== 90 || nRotationDeg
== 270 )
139 && ( nElevationDeg
== 90 || nElevationDeg
== 270 ) )
148 if( (sin(R
)*sin(E
))>0 )
153 else if( (nRotationDeg
== 0 || nRotationDeg
== 180 )
154 && ( nElevationDeg
== 0 || nElevationDeg
== 180 ) )
161 else if( ( nRotationDeg
== 90 || nRotationDeg
== 270 )
162 && ( nElevationDeg
== 0 || nElevationDeg
== 180 ) )
167 if( (sin(R
)/cos(E
))>0 )
177 else if ( nElevationDeg
== 0 || nElevationDeg
== 180 )
183 //use element 13 for sign
184 if((cos(x
)*sin(y
)*sin(R
))<0.0)
187 else if ( nElevationDeg
== 90 || nElevationDeg
== 270 )
190 //element 12 + 22 --> y=0 or M_PI and x=+-M_PI/2
192 z
= atan(sin(R
)/(cos(R
)*sin(E
)));
193 //use element 13 for sign for x
194 if( (sin(R
)*sin(z
))>0.0 )
198 //use element 21 for y
199 if( (sin(R
)*sin(E
)*sin(z
))>0.0)
204 else if ( nRotationDeg
== 0 || nRotationDeg
== 180 )
210 double f23
= cos(R
)*sin(E
);
211 if( (f23
* sin(x
)) < 0.0 )
214 else if (nRotationDeg
== 90 || nRotationDeg
== 270)
223 x
*= -1.0; //different signs for x and z
226 double cy
= sR
*sin(E
)/sin(z
);
227 lcl_ensureIntervalMinus1To1(cy
);
230 //use element 22 for sign:
231 if( (sin(x
)*sin(y
)*sin(z
)*cos(E
))<0.0)
236 z
= atan(tan(R
) * sin(E
));
239 OSL_FAIL("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
242 double cy
= cos(R
)/cos(z
);
243 lcl_ensureIntervalMinus1To1(cy
);
247 double fDenominator
= cos(z
)*(1.0-pow(sin(y
),2));
248 if(fDenominator
==0.0)
250 OSL_FAIL("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
253 double sx
= cos(R
)*sin(E
)/fDenominator
;
254 lcl_ensureIntervalMinus1To1(sx
);
257 //use element 13 for sign:
258 double f13a
= cos(x
)*cos(z
)*sin(y
);
259 double f13b
= sin(R
)-sx
*sin(z
);
260 if( (f13b
*f13a
)<0.0 )
263 //use element 22 for further investigations:
266 double f22a
= cos(x
)*cos(z
);
267 double f22b
= cos(E
)-(sx
*sin(y
)*sin(z
));
268 if( (f22a
*f22b
)<0.0 )
276 //change nothing or both
277 //use element 22 for further investigations:
278 double f22a
= cos(x
)*cos(z
);
279 double f22b
= cos(E
)-(sx
*sin(y
)*sin(z
));
280 if( (f22a
*f22b
)<0.0 )
289 void ThreeDHelper::convertXYZAngleRadToElevationRotationDeg(
290 sal_Int32
& rnElevationDeg
, sal_Int32
& rnRotationDeg
,
291 double fXRad
, double fYRad
, double fZRad
)
293 // for a description of the algorithm see issue 72994
294 //https://bz.apache.org/ooo/show_bug.cgi?id=72994
295 //https://bz.apache.org/ooo/attachment.cgi?id=50608
297 double R
= 0.0; //Rotation in Rad
298 double E
= 0.0; //Elevation in Rad
304 double f11
= cos(y
)*cos(z
);
306 if( lcl_isSinZero(y
) )
310 if( lcl_isCosZero(x
) )
312 //siny == 0 && cosx == 0
314 if( lcl_isSinZero(z
) )
316 //siny == 0 && cosx == 0 && sinz == 0
317 //example: x=+-90 y=0oder180 z=0(oder180)
326 double f23
= cos(z
)*sin(x
) / cos(R
);
332 else if( lcl_isCosZero(z
) )
334 //siny == 0 && cosx == 0 && cosz == 0
335 //example: x=+-90 y=0oder180 z=+-90
337 double f13
= sin(x
)*sin(z
);
345 double f21
= cos(y
)*sin(z
) / sin(R
);
353 //siny == 0 && cosx == 0 && cosz != 0 && sinz != 0
355 double f13
= sin(x
)*sin(z
);
362 double f23
= cos(z
)*sin(x
);
369 else if( lcl_isSinZero(x
) )
378 double f22
= cos(x
)*cos(z
);
384 else if( lcl_isSinZero(z
) )
386 //sinY==0 sinZ==0 sinx!=0 cosx!=0
394 double f22
= cos(x
)*cos(z
);
395 double f23
= cos(z
)*sin(x
);
396 E
= atan( f23
/(f22
*cos(R
)) );
400 else if( lcl_isCosZero(z
) )
402 //sinY == 0 && cosZ == 0 && cosx != 0 && sinx != 0
403 double f13
= sin(x
)*sin(z
);
411 double f21
= cos(y
)*sin(z
);
419 //sinY == 0 && all other !=0
420 double f13
= sin(x
)*sin(z
);
422 if( (f11
*cos(R
))<0.0 )
425 double f22
= cos(x
)*cos(z
);
426 if( !lcl_isCosZero(R
) )
427 E
= atan( cos(z
)*sin(x
) /( f22
*cos(R
) ) );
429 E
= atan( cos(y
)*sin(z
) /( f22
*sin(R
) ) );
434 else if( lcl_isCosZero(y
) )
438 double f13
= sin(x
)*sin(z
)+cos(x
)*cos(z
)*sin(y
);
444 double f22
= cos(x
)*cos(z
)+sin(x
)*sin(y
)*sin(z
);
450 else if( lcl_isSinZero(x
) )
452 //cosY!=0 sinY!=0 sinX=0
453 if( lcl_isSinZero(z
) )
455 //cosY!=0 sinY!=0 sinX=0 sinZ=0
456 double f13
= cos(x
)*cos(z
)*sin(y
);
462 double f22
= cos(x
)*cos(z
);
468 else if( lcl_isCosZero(z
) )
470 //cosY!=0 sinY!=0 sinX=0 cosZ=0
474 double f23
= -1.0*cos(x
)*sin(y
)*sin(z
);
475 if( (f23
*cos(R
)*sin(E
))<0.0 )
483 //cosY!=0 sinY!=0 sinX=0 sinZ!=0 cosZ!=0
484 double f13
= cos(x
)*cos(z
)*sin(y
);
490 double f21
= cos(y
)*sin(z
);
491 double f22
= cos(x
)*cos(z
);
492 E
= atan(f21
/(f22
*sin(R
)) );
494 if( (f22
*cos(E
))<0.0 )
498 else if( lcl_isCosZero(x
) )
500 //cosY!=0 sinY!=0 cosX=0
502 if( lcl_isSinZero(z
) )
504 //cosY!=0 sinY!=0 cosX=0 sinZ=0
505 R
=0;//13 -> R=0 or M_PI
508 E
=M_PI_2
;//22 -> E=+-M_PI/2
509 //use element 11 and 23 for sign
510 double f23
= cos(z
)*sin(x
);
511 if( (f11
*f23
*sin(E
))<0.0 )
514 else if( lcl_isCosZero(z
) )
516 //cosY!=0 sinY!=0 cosX=0 cosZ=0
518 if( (sin(x
)*sin(z
))>0.0 )
523 E
=acos( sin(x
)*sin(y
)*sin(z
));
524 //use element 21 for sign:
525 if( (cos(y
)*sin(z
)*sin(R
)*sin(E
))<0.0 )
530 //cosY!=0 sinY!=0 cosX=0 sinZ!=0 cosZ!=0
532 R
= atan( sin(x
)*sin(z
)/(cos(y
)*cos(z
)) );
534 if( (sin(x
)*sin(z
))<0.0 )
537 E
= acos(sin(x
)*sin(y
)*sin(z
) );
539 if( (cos(y
)*sin(z
)*sin(R
)*sin(E
))<0.0 )
543 else if( lcl_isSinZero(z
) )
545 //cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ=0
548 //use element 13 for sign
549 if( (cos(x
)*cos(z
)*sin(y
)*sin(R
))<0.0 )
552 E
= acos( cos(x
)*cos(z
) );
553 //use element 23 for sign
554 if( (cos(z
)*sin(x
)*cos(R
)*sin(E
))<0.0 )
557 else if( lcl_isCosZero(z
) )
559 //cosY!=0 sinY!=0 sinX!=0 cosX!=0 cosZ=0
561 R
=atan(-cos(y
)/(cos(x
)*sin(y
)));
562 //use element 13 for 'sign'
563 if( (sin(x
)*sin(z
)*sin(R
))<0.0 )
566 E
=atan( cos(y
)*sin(z
)/(sin(R
)*sin(x
)*sin(y
)*sin(z
)) );
567 //use element 23 for 'sign'
568 if( (-cos(x
)*sin(y
)*sin(z
)*cos(R
)*sin(E
))<0.0 )
573 //cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ!=0 cosZ!=0
575 double f13
= sin(x
)*sin(z
)+cos(x
)*cos(z
)*sin(y
);
576 R
= atan( f13
/ f11
);
579 double f22
= cos(x
)*cos(z
)+sin(x
)*sin(y
)*sin(z
);
580 double f23
= cos(x
)*sin(y
)*sin(z
)-cos(z
)*sin(x
);
582 E
= atan( -1.0*f23
/(f22
*cos(R
)) );
587 rnElevationDeg
= basegfx::fround(basegfx::rad2deg(E
));
588 rnRotationDeg
= basegfx::fround(basegfx::rad2deg(R
));
591 double ThreeDHelper::getValueClippedToRange( double fAngle
, const double& fPositivLimit
)
593 if( fAngle
<-1*fPositivLimit
)
594 fAngle
=-1*fPositivLimit
;
595 else if( fAngle
>fPositivLimit
)
596 fAngle
=fPositivLimit
;
600 void ThreeDHelper::adaptRadAnglesForRightAngledAxes( double& rfXAngleRad
, double& rfYAngleRad
)
602 rfXAngleRad
= ThreeDHelper::getValueClippedToRange(rfXAngleRad
, basegfx::deg2rad(ThreeDHelper::getXDegreeAngleLimitForRightAngledAxes()) );
603 rfYAngleRad
= ThreeDHelper::getValueClippedToRange(rfYAngleRad
, basegfx::deg2rad(ThreeDHelper::getYDegreeAngleLimitForRightAngledAxes()) );
607 void ThreeDHelper::getCameraDistanceRange( double& rfMinimumDistance
, double& rfMaximumDistance
)
609 rfMinimumDistance
= 3.0/4.0*FIXED_SIZE_FOR_3D_CHART_VOLUME
;//empiric value
610 rfMaximumDistance
= 20.0*FIXED_SIZE_FOR_3D_CHART_VOLUME
;//empiric value
613 void ThreeDHelper::ensureCameraDistanceRange( double& rfCameraDistance
)
616 getCameraDistanceRange( fMin
, fMax
);
617 if( rfCameraDistance
< fMin
)
618 rfCameraDistance
= fMin
;
619 if( rfCameraDistance
> fMax
)
620 rfCameraDistance
= fMax
;
623 double ThreeDHelper::CameraDistanceToPerspective( double fCameraDistance
)
626 ThreeDHelper::getCameraDistanceRange( fMin
, fMax
);
627 //fMax <-> 0; fMin <->100
629 double a
= 100.0*fMax
*fMin
/(fMax
-fMin
);
632 double fRet
= a
/fCameraDistance
+ b
;
637 double ThreeDHelper::PerspectiveToCameraDistance( double fPerspective
)
640 ThreeDHelper::getCameraDistanceRange( fMin
, fMax
);
641 //fMax <-> 0; fMin <->100
643 double a
= 100.0*fMax
*fMin
/(fMax
-fMin
);
646 double fRet
= a
/(fPerspective
- b
);
651 void ThreeDHelper::getRoundedEdgesAndObjectLines(
652 const rtl::Reference
< Diagram
> & xDiagram
653 , sal_Int32
& rnRoundedEdges
, sal_Int32
& rnObjectLines
)
659 bool bDifferentRoundedEdges
= false;
660 bool bDifferentObjectLines
= false;
662 drawing::LineStyle
aLineStyle( drawing::LineStyle_SOLID
);
664 std::vector
< rtl::Reference
< DataSeries
> > aSeriesList
=
665 xDiagram
->getDataSeries();
666 sal_Int32 nSeriesCount
= static_cast<sal_Int32
>( aSeriesList
.size() );
668 OUString
aPercentDiagonalPropertyName( u
"PercentDiagonal"_ustr
);
669 OUString
aBorderStylePropertyName( u
"BorderStyle"_ustr
);
671 for( sal_Int32 nS
= 0; nS
< nSeriesCount
; ++nS
)
673 const rtl::Reference
< DataSeries
>& xSeries( aSeriesList
[nS
] );
679 sal_Int16 nPercentDiagonal
= 0;
681 xSeries
->getPropertyValue( aPercentDiagonalPropertyName
) >>= nPercentDiagonal
;
682 rnRoundedEdges
= static_cast< sal_Int32
>( nPercentDiagonal
);
684 if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
685 , aPercentDiagonalPropertyName
, uno::Any(nPercentDiagonal
) ) )
686 bDifferentRoundedEdges
= true;
688 catch( const uno::Exception
& )
690 TOOLS_WARN_EXCEPTION("chart2", "" );
691 bDifferentRoundedEdges
= true;
695 xSeries
->getPropertyValue( aBorderStylePropertyName
) >>= aLineStyle
;
697 if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
698 , aBorderStylePropertyName
, uno::Any(aLineStyle
) ) )
699 bDifferentObjectLines
= true;
701 catch( const uno::Exception
& )
703 TOOLS_WARN_EXCEPTION("chart2", "" );
704 bDifferentObjectLines
= true;
709 if( !bDifferentRoundedEdges
)
711 sal_Int16 nPercentDiagonal
= 0;
712 xSeries
->getPropertyValue( aPercentDiagonalPropertyName
) >>= nPercentDiagonal
;
713 sal_Int32 nCurrentRoundedEdges
= static_cast< sal_Int32
>( nPercentDiagonal
);
714 if(nCurrentRoundedEdges
!=rnRoundedEdges
715 || DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
716 , aPercentDiagonalPropertyName
, uno::Any( static_cast< sal_Int16
>(rnRoundedEdges
) ) ) )
718 bDifferentRoundedEdges
= true;
722 if( !bDifferentObjectLines
)
724 drawing::LineStyle aCurrentLineStyle
;
725 xSeries
->getPropertyValue( aBorderStylePropertyName
) >>= aCurrentLineStyle
;
726 if(aCurrentLineStyle
!=aLineStyle
727 || DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
728 , aBorderStylePropertyName
, uno::Any(aLineStyle
) ) )
729 bDifferentObjectLines
= true;
732 if( bDifferentRoundedEdges
&& bDifferentObjectLines
)
738 if( bDifferentObjectLines
)
740 else if( aLineStyle
== drawing::LineStyle_SOLID
)
743 catch( const uno::Exception
& )
745 TOOLS_WARN_EXCEPTION("chart2", "" );
749 void ThreeDHelper::setRoundedEdgesAndObjectLines(
750 const rtl::Reference
< Diagram
> & xDiagram
751 , sal_Int32 nRoundedEdges
, sal_Int32 nObjectLines
)
753 if( (nRoundedEdges
<0||nRoundedEdges
>100) && nObjectLines
!=0 && nObjectLines
!=1 )
756 drawing::LineStyle
aLineStyle( drawing::LineStyle_NONE
);
758 aLineStyle
= drawing::LineStyle_SOLID
;
760 uno::Any
aALineStyle( aLineStyle
);
761 uno::Any
aARoundedEdges( static_cast< sal_Int16
>( nRoundedEdges
));
763 std::vector
< rtl::Reference
< DataSeries
> > aSeriesList
=
764 xDiagram
->getDataSeries();
765 for( auto const& xSeries
: aSeriesList
)
767 if( nRoundedEdges
>=0 && nRoundedEdges
<=100 )
768 DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries
, u
"PercentDiagonal"_ustr
, aARoundedEdges
);
770 if( nObjectLines
==0 || nObjectLines
==1 )
771 DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries
, u
"BorderStyle"_ustr
, aALineStyle
);
775 CuboidPlanePosition
ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( const rtl::Reference
< ::chart::Diagram
>& xDiagram
)
777 CuboidPlanePosition
eRet(CuboidPlanePosition_Left
);
779 double fXAngleRad
=0.0; double fYAngleRad
=0.0; double fZAngleRad
=0.0;
780 xDiagram
->getRotationAngle( fXAngleRad
, fYAngleRad
, fZAngleRad
);
781 if( lcl_isRightAngledAxesSetAndSupported( xDiagram
) )
783 ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad
, fYAngleRad
);
785 if( sin(fYAngleRad
)>0.0 )
786 eRet
= CuboidPlanePosition_Right
;
790 CuboidPlanePosition
ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( const rtl::Reference
< Diagram
>& xDiagram
)
792 CuboidPlanePosition
eRet(CuboidPlanePosition_Back
);
794 double fXAngleRad
=0.0; double fYAngleRad
=0.0; double fZAngleRad
=0.0;
795 xDiagram
->getRotationAngle( fXAngleRad
, fYAngleRad
, fZAngleRad
);
796 if( lcl_isRightAngledAxesSetAndSupported( xDiagram
) )
798 ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad
, fYAngleRad
);
800 if( cos(fXAngleRad
)*cos(fYAngleRad
)<0.0 )
801 eRet
= CuboidPlanePosition_Front
;
805 CuboidPlanePosition
ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( const rtl::Reference
< Diagram
>& xDiagram
)
807 CuboidPlanePosition
eRet(CuboidPlanePosition_Bottom
);
809 double fXAngleRad
=0.0; double fYAngleRad
=0.0; double fZAngleRad
=0.0;
810 xDiagram
->getRotationAngle( fXAngleRad
, fYAngleRad
, fZAngleRad
);
811 if( lcl_isRightAngledAxesSetAndSupported( xDiagram
) )
813 ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad
, fYAngleRad
);
815 if( sin(fXAngleRad
)*cos(fYAngleRad
)<0.0 )
816 eRet
= CuboidPlanePosition_Top
;
822 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */