30e72a9a36
Change-Id: Ie41b33c09d79b40116cc10f7557987860acb55e8
1497 lines
50 KiB
C++
1497 lines
50 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include "ThreeDHelper.hxx"
|
|
#include "macros.hxx"
|
|
#include "DiagramHelper.hxx"
|
|
#include "ChartTypeHelper.hxx"
|
|
#include "BaseGFXHelper.hxx"
|
|
#include "DataSeriesHelper.hxx"
|
|
#include "defines.hxx"
|
|
|
|
#include <editeng/unoprnms.hxx>
|
|
#include <com/sun/star/beans/XPropertyState.hpp>
|
|
#include <com/sun/star/chart2/XDiagram.hpp>
|
|
#include <com/sun/star/drawing/LineStyle.hpp>
|
|
|
|
namespace chart
|
|
{
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::chart2;
|
|
|
|
using ::com::sun::star::uno::Reference;
|
|
using ::com::sun::star::uno::Sequence;
|
|
using ::rtl::math::cos;
|
|
using ::rtl::math::sin;
|
|
using ::rtl::math::tan;
|
|
|
|
namespace
|
|
{
|
|
|
|
bool lcl_isRightAngledAxesSetAndSupported( const Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
sal_Bool bRightAngledAxes = sal_False;
|
|
if( xSceneProperties.is() )
|
|
{
|
|
xSceneProperties->getPropertyValue( "RightAngledAxes") >>= bRightAngledAxes;
|
|
if(bRightAngledAxes)
|
|
{
|
|
uno::Reference< chart2::XDiagram > xDiagram( xSceneProperties, uno::UNO_QUERY );
|
|
if( ChartTypeHelper::isSupportingRightAngledAxes(
|
|
DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void lcl_RotateLightSource( const Reference< beans::XPropertySet >& xSceneProperties
|
|
, const OUString& rLightSourceDirection
|
|
, const OUString& rLightSourceOn
|
|
, const ::basegfx::B3DHomMatrix& rRotationMatrix )
|
|
{
|
|
if( xSceneProperties.is() )
|
|
{
|
|
sal_Bool bLightOn = sal_False;
|
|
if( xSceneProperties->getPropertyValue( rLightSourceOn ) >>= bLightOn )
|
|
{
|
|
if( bLightOn )
|
|
{
|
|
drawing::Direction3D aLight;
|
|
if( xSceneProperties->getPropertyValue( rLightSourceDirection ) >>= aLight )
|
|
{
|
|
::basegfx::B3DVector aLightVector( BaseGFXHelper::Direction3DToB3DVector( aLight ) );
|
|
aLightVector = rRotationMatrix*aLightVector;
|
|
|
|
xSceneProperties->setPropertyValue( rLightSourceDirection
|
|
, uno::makeAny( BaseGFXHelper::B3DVectorToDirection3D( aLightVector ) ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcl_rotateLights( const ::basegfx::B3DHomMatrix& rLightRottion, const Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
if(!xSceneProperties.is())
|
|
return;
|
|
|
|
::basegfx::B3DHomMatrix aLightRottion( rLightRottion );
|
|
BaseGFXHelper::ReduceToRotationMatrix( aLightRottion );
|
|
|
|
lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection1", "D3DSceneLightOn1", aLightRottion );
|
|
lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection2", "D3DSceneLightOn2", aLightRottion );
|
|
lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection3", "D3DSceneLightOn3", aLightRottion );
|
|
lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection4", "D3DSceneLightOn4", aLightRottion );
|
|
lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection5", "D3DSceneLightOn5", aLightRottion );
|
|
lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection6", "D3DSceneLightOn6", aLightRottion );
|
|
lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection7", "D3DSceneLightOn7", aLightRottion );
|
|
lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection8", "D3DSceneLightOn8", aLightRottion );
|
|
}
|
|
|
|
::basegfx::B3DHomMatrix lcl_getInverseRotationMatrix( const Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
::basegfx::B3DHomMatrix aInverseRotation;
|
|
double fXAngleRad=0.0;
|
|
double fYAngleRad=0.0;
|
|
double fZAngleRad=0.0;
|
|
ThreeDHelper::getRotationAngleFromDiagram(
|
|
xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
|
|
aInverseRotation.rotate( 0.0, 0.0, -fZAngleRad );
|
|
aInverseRotation.rotate( 0.0, -fYAngleRad, 0.0 );
|
|
aInverseRotation.rotate( -fXAngleRad, 0.0, 0.0 );
|
|
return aInverseRotation;
|
|
}
|
|
|
|
::basegfx::B3DHomMatrix lcl_getCompleteRotationMatrix( const Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
::basegfx::B3DHomMatrix aCompleteRotation;
|
|
double fXAngleRad=0.0;
|
|
double fYAngleRad=0.0;
|
|
double fZAngleRad=0.0;
|
|
ThreeDHelper::getRotationAngleFromDiagram(
|
|
xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
|
|
aCompleteRotation.rotate( fXAngleRad, fYAngleRad, fZAngleRad );
|
|
return aCompleteRotation;
|
|
}
|
|
|
|
bool lcl_isEqual( const drawing::Direction3D& rA, const drawing::Direction3D& rB )
|
|
{
|
|
return ::rtl::math::approxEqual(rA.DirectionX, rB.DirectionX)
|
|
&& ::rtl::math::approxEqual(rA.DirectionY, rB.DirectionY)
|
|
&& ::rtl::math::approxEqual(rA.DirectionZ, rB.DirectionZ);
|
|
}
|
|
|
|
bool lcl_isLightScheme( const uno::Reference< beans::XPropertySet >& xDiagramProps, bool bRealistic )
|
|
{
|
|
if(!xDiagramProps.is())
|
|
return false;
|
|
|
|
sal_Bool bIsOn = sal_False;
|
|
xDiagramProps->getPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_2 ) >>= bIsOn;
|
|
if(!bIsOn)
|
|
return false;
|
|
|
|
uno::Reference< chart2::XDiagram > xDiagram( xDiagramProps, uno::UNO_QUERY );
|
|
uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
|
|
|
|
sal_Int32 nColor = 0;
|
|
xDiagramProps->getPropertyValue( UNO_NAME_3D_SCENE_LIGHTCOLOR_2 ) >>= nColor;
|
|
if( nColor != ::chart::ChartTypeHelper::getDefaultDirectLightColor( !bRealistic, xChartType ) )
|
|
return false;
|
|
|
|
sal_Int32 nAmbientColor = 0;
|
|
xDiagramProps->getPropertyValue( UNO_NAME_3D_SCENE_AMBIENTCOLOR ) >>= nAmbientColor;
|
|
if( nAmbientColor != ::chart::ChartTypeHelper::getDefaultAmbientLightColor( !bRealistic, xChartType ) )
|
|
return false;
|
|
|
|
drawing::Direction3D aDirection(0,0,0);
|
|
xDiagramProps->getPropertyValue( UNO_NAME_3D_SCENE_LIGHTDIRECTION_2 ) >>= aDirection;
|
|
|
|
drawing::Direction3D aDefaultDirection( bRealistic
|
|
? ChartTypeHelper::getDefaultRealisticLightDirection(xChartType)
|
|
: ChartTypeHelper::getDefaultSimpleLightDirection(xChartType) );
|
|
|
|
//rotate default light direction when right angled axes are off but supported
|
|
{
|
|
sal_Bool bRightAngledAxes = sal_False;
|
|
xDiagramProps->getPropertyValue( "RightAngledAxes") >>= bRightAngledAxes;
|
|
if(!bRightAngledAxes)
|
|
{
|
|
if( ChartTypeHelper::isSupportingRightAngledAxes(
|
|
DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) ) )
|
|
{
|
|
::basegfx::B3DHomMatrix aRotation( lcl_getCompleteRotationMatrix( xDiagramProps ) );
|
|
BaseGFXHelper::ReduceToRotationMatrix( aRotation );
|
|
::basegfx::B3DVector aLightVector( BaseGFXHelper::Direction3DToB3DVector( aDefaultDirection ) );
|
|
aLightVector = aRotation*aLightVector;
|
|
aDefaultDirection = BaseGFXHelper::B3DVectorToDirection3D( aLightVector );
|
|
}
|
|
}
|
|
}
|
|
|
|
return lcl_isEqual( aDirection, aDefaultDirection );
|
|
}
|
|
|
|
bool lcl_isRealisticLightScheme( const uno::Reference< beans::XPropertySet >& xDiagramProps )
|
|
{
|
|
return lcl_isLightScheme( xDiagramProps, true /*bRealistic*/ );
|
|
}
|
|
bool lcl_isSimpleLightScheme( const uno::Reference< beans::XPropertySet >& xDiagramProps )
|
|
{
|
|
return lcl_isLightScheme( xDiagramProps, false /*bRealistic*/ );
|
|
}
|
|
void lcl_setLightsForScheme( const uno::Reference< beans::XPropertySet >& xDiagramProps, const ThreeDLookScheme& rScheme )
|
|
{
|
|
if(!xDiagramProps.is())
|
|
return;
|
|
if( rScheme == ThreeDLookScheme_Unknown)
|
|
return;
|
|
|
|
xDiagramProps->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_2, uno::makeAny( sal_True ) );
|
|
|
|
uno::Reference< chart2::XDiagram > xDiagram( xDiagramProps, uno::UNO_QUERY );
|
|
uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
|
|
uno::Any aADirection( uno::makeAny( rScheme == ThreeDLookScheme_Simple
|
|
? ChartTypeHelper::getDefaultSimpleLightDirection(xChartType)
|
|
: ChartTypeHelper::getDefaultRealisticLightDirection(xChartType) ) );
|
|
|
|
xDiagramProps->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTDIRECTION_2, aADirection );
|
|
//rotate light direction when right angled axes are off but supported
|
|
{
|
|
sal_Bool bRightAngledAxes = sal_False;
|
|
xDiagramProps->getPropertyValue( "RightAngledAxes") >>= bRightAngledAxes;
|
|
if(!bRightAngledAxes)
|
|
{
|
|
if( ChartTypeHelper::isSupportingRightAngledAxes( xChartType ) )
|
|
{
|
|
::basegfx::B3DHomMatrix aRotation( lcl_getCompleteRotationMatrix( xDiagramProps ) );
|
|
BaseGFXHelper::ReduceToRotationMatrix( aRotation );
|
|
lcl_RotateLightSource( xDiagramProps, "D3DSceneLightDirection2", "D3DSceneLightOn2", aRotation );
|
|
}
|
|
}
|
|
}
|
|
|
|
sal_Int32 nColor = ::chart::ChartTypeHelper::getDefaultDirectLightColor( rScheme==ThreeDLookScheme_Simple, xChartType );
|
|
xDiagramProps->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTCOLOR_2, uno::makeAny( nColor ) );
|
|
|
|
sal_Int32 nAmbientColor = ::chart::ChartTypeHelper::getDefaultAmbientLightColor( rScheme==ThreeDLookScheme_Simple, xChartType );
|
|
xDiagramProps->setPropertyValue( UNO_NAME_3D_SCENE_AMBIENTCOLOR, uno::makeAny( nAmbientColor ) );
|
|
}
|
|
|
|
bool lcl_isRealisticScheme( drawing::ShadeMode aShadeMode
|
|
, sal_Int32 nRoundedEdges
|
|
, sal_Int32 nObjectLines )
|
|
{
|
|
if(aShadeMode!=drawing::ShadeMode_SMOOTH)
|
|
return false;
|
|
if(nRoundedEdges!=5)
|
|
return false;
|
|
if(nObjectLines!=0)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool lcl_isSimpleScheme( drawing::ShadeMode aShadeMode
|
|
, sal_Int32 nRoundedEdges
|
|
, sal_Int32 nObjectLines
|
|
, const uno::Reference< XDiagram >& xDiagram )
|
|
{
|
|
if(aShadeMode!=drawing::ShadeMode_FLAT)
|
|
return false;
|
|
if(nRoundedEdges!=0)
|
|
return false;
|
|
if(nObjectLines==0)
|
|
{
|
|
uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
|
|
return ChartTypeHelper::noBordersForSimpleScheme( xChartType );
|
|
}
|
|
if(nObjectLines!=1)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void lcl_setRealisticScheme( drawing::ShadeMode& rShadeMode
|
|
, sal_Int32& rnRoundedEdges
|
|
, sal_Int32& rnObjectLines )
|
|
{
|
|
rShadeMode = drawing::ShadeMode_SMOOTH;
|
|
rnRoundedEdges = 5;
|
|
rnObjectLines = 0;
|
|
}
|
|
|
|
void lcl_setSimpleScheme( drawing::ShadeMode& rShadeMode
|
|
, sal_Int32& rnRoundedEdges
|
|
, sal_Int32& rnObjectLines
|
|
, const uno::Reference< XDiagram >& xDiagram )
|
|
{
|
|
rShadeMode = drawing::ShadeMode_FLAT;
|
|
rnRoundedEdges = 0;
|
|
|
|
uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
|
|
rnObjectLines = ChartTypeHelper::noBordersForSimpleScheme( xChartType ) ? 0 : 1;
|
|
}
|
|
|
|
} //end anonymous namespace
|
|
|
|
drawing::CameraGeometry ThreeDHelper::getDefaultCameraGeometry( bool bPie )
|
|
{
|
|
// ViewReferencePoint (Point on the View plane)
|
|
drawing::Position3D vrp(17634.6218373783, 10271.4823817647, 24594.8639082739);
|
|
// ViewPlaneNormal (Normal to the View Plane)
|
|
drawing::Direction3D vpn(0.416199821709347, 0.173649045905254, 0.892537795986984);
|
|
// ViewUpVector (determines the v-axis direction on the view plane as
|
|
// projection of VUP parallel to VPN onto th view pane)
|
|
drawing::Direction3D vup(-0.0733876362771618, 0.984807599917971, -0.157379306090273);
|
|
|
|
if( bPie )
|
|
{
|
|
vrp = drawing::Position3D( 0.0, 0.0, 87591.2408759124 );//--> 5 percent perspecitve
|
|
vpn = drawing::Direction3D( 0.0, 0.0, 1.0 );
|
|
vup = drawing::Direction3D( 0.0, 1.0, 0.0 );
|
|
}
|
|
|
|
return drawing::CameraGeometry( vrp, vpn, vup );
|
|
}
|
|
|
|
namespace
|
|
{
|
|
::basegfx::B3DHomMatrix lcl_getCameraMatrix( const uno::Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
drawing::HomogenMatrix aCameraMatrix;
|
|
|
|
drawing::CameraGeometry aCG( ThreeDHelper::getDefaultCameraGeometry() );
|
|
if( xSceneProperties.is() )
|
|
xSceneProperties->getPropertyValue( "D3DCameraGeometry" ) >>= aCG;
|
|
|
|
::basegfx::B3DVector aVPN( BaseGFXHelper::Direction3DToB3DVector( aCG.vpn ) );
|
|
::basegfx::B3DVector aVUP( BaseGFXHelper::Direction3DToB3DVector( aCG.vup ) );
|
|
|
|
//normalize vectors:
|
|
aVPN.normalize();
|
|
aVUP.normalize();
|
|
|
|
::basegfx::B3DVector aCross = ::basegfx::cross( aVUP, aVPN );
|
|
|
|
//first line is VUP x VPN
|
|
aCameraMatrix.Line1.Column1 = aCross[0];
|
|
aCameraMatrix.Line1.Column2 = aCross[1];
|
|
aCameraMatrix.Line1.Column3 = aCross[2];
|
|
aCameraMatrix.Line1.Column4 = 0.0;
|
|
|
|
//second line is VUP
|
|
aCameraMatrix.Line2.Column1 = aVUP[0];
|
|
aCameraMatrix.Line2.Column2 = aVUP[1];
|
|
aCameraMatrix.Line2.Column3 = aVUP[2];
|
|
aCameraMatrix.Line2.Column4 = 0.0;
|
|
|
|
//third line is VPN
|
|
aCameraMatrix.Line3.Column1 = aVPN[0];
|
|
aCameraMatrix.Line3.Column2 = aVPN[1];
|
|
aCameraMatrix.Line3.Column3 = aVPN[2];
|
|
aCameraMatrix.Line3.Column4 = 0.0;
|
|
|
|
//fourth line is 0 0 0 1
|
|
aCameraMatrix.Line4.Column1 = 0.0;
|
|
aCameraMatrix.Line4.Column2 = 0.0;
|
|
aCameraMatrix.Line4.Column3 = 0.0;
|
|
aCameraMatrix.Line4.Column4 = 1.0;
|
|
|
|
return BaseGFXHelper::HomogenMatrixToB3DHomMatrix( aCameraMatrix );
|
|
}
|
|
|
|
double lcl_shiftAngleToIntervalMinusPiToPi( double fAngleRad )
|
|
{
|
|
//valid range: ]-Pi,Pi]
|
|
while( fAngleRad<=-F_PI )
|
|
fAngleRad+=(2*F_PI);
|
|
while( fAngleRad>F_PI )
|
|
fAngleRad-=(2*F_PI);
|
|
return fAngleRad;
|
|
}
|
|
|
|
void lcl_shiftAngleToIntervalMinus180To180( sal_Int32& rnAngleDegree )
|
|
{
|
|
//valid range: ]-180,180]
|
|
while( rnAngleDegree<=-180 )
|
|
rnAngleDegree+=360;
|
|
while( rnAngleDegree>180 )
|
|
rnAngleDegree-=360;
|
|
}
|
|
|
|
void lcl_shiftAngleToIntervalZeroTo360( sal_Int32& rnAngleDegree )
|
|
{
|
|
//valid range: [0,360[
|
|
while( rnAngleDegree<0 )
|
|
rnAngleDegree+=360;
|
|
while( rnAngleDegree>=360 )
|
|
rnAngleDegree-=360;
|
|
}
|
|
|
|
void lcl_ensureIntervalMinus1To1( double& rSinOrCos )
|
|
{
|
|
if (rSinOrCos < -1.0)
|
|
rSinOrCos = -1.0;
|
|
else if (rSinOrCos > 1.0)
|
|
rSinOrCos = 1.0;
|
|
}
|
|
|
|
bool lcl_isSinZero( double fAngleRad )
|
|
{
|
|
return ::basegfx::fTools::equalZero( sin(fAngleRad), 0.0000001 );
|
|
}
|
|
bool lcl_isCosZero( double fAngleRad )
|
|
{
|
|
return ::basegfx::fTools::equalZero( cos(fAngleRad), 0.0000001 );
|
|
}
|
|
|
|
}
|
|
|
|
void ThreeDHelper::convertElevationRotationDegToXYZAngleRad(
|
|
sal_Int32 nElevationDeg, sal_Int32 nRotationDeg,
|
|
double& rfXAngleRad, double& rfYAngleRad, double& rfZAngleRad)
|
|
{
|
|
// for a description of the algorithm see issue 72994
|
|
//http://www.openoffice.org/issues/show_bug.cgi?id=72994
|
|
//http://www.openoffice.org/nonav/issues/showattachment.cgi/50608/DescriptionCorrected.odt
|
|
|
|
lcl_shiftAngleToIntervalZeroTo360( nElevationDeg );
|
|
lcl_shiftAngleToIntervalZeroTo360( nRotationDeg );
|
|
|
|
double& x = rfXAngleRad;
|
|
double& y = rfYAngleRad;
|
|
double& z = rfZAngleRad;
|
|
|
|
double E = F_PI*nElevationDeg/180; //elevation in Rad
|
|
double R = F_PI*nRotationDeg/180; //rotation in Rad
|
|
|
|
if( (nRotationDeg == 0 || nRotationDeg == 180 )
|
|
&& ( nElevationDeg == 90 || nElevationDeg == 270 ) )
|
|
{
|
|
//sR==0 && cE==0
|
|
z = 0.0;
|
|
//element 23
|
|
double f23 = cos(R)*sin(E);
|
|
if(f23>0)
|
|
x = F_PI/2;
|
|
else
|
|
x = -F_PI/2;
|
|
y = R;
|
|
}
|
|
else if( ( nRotationDeg == 90 || nRotationDeg == 270 )
|
|
&& ( nElevationDeg == 90 || nElevationDeg == 270 ) )
|
|
{
|
|
//cR==0 && cE==0
|
|
z = F_PI/2;
|
|
if( sin(R)>0 )
|
|
x = F_PI/2.0;
|
|
else
|
|
x = -F_PI/2.0;
|
|
|
|
if( (sin(R)*sin(E))>0 )
|
|
y = 0.0;
|
|
else
|
|
y = F_PI;
|
|
}
|
|
else if( (nRotationDeg == 0 || nRotationDeg == 180 )
|
|
&& ( nElevationDeg == 0 || nElevationDeg == 180 ) )
|
|
{
|
|
//sR==0 && sE==0
|
|
z = 0.0;
|
|
y = R;
|
|
x = E;
|
|
}
|
|
else if( ( nRotationDeg == 90 || nRotationDeg == 270 )
|
|
&& ( nElevationDeg == 0 || nElevationDeg == 180 ) )
|
|
{
|
|
//cR==0 && sE==0
|
|
z = 0.0;
|
|
|
|
if( (sin(R)/cos(E))>0 )
|
|
y = F_PI/2;
|
|
else
|
|
y = -F_PI/2;
|
|
|
|
if( (cos(E))>0 )
|
|
x = 0;
|
|
else
|
|
x = F_PI;
|
|
}
|
|
else if ( nElevationDeg == 0 || nElevationDeg == 180 )
|
|
{
|
|
//sR!=0 cR!=0 sE==0
|
|
z = 0.0;
|
|
x = E;
|
|
y = R;
|
|
//use element 13 for sign
|
|
if((cos(x)*sin(y)*sin(R))<0.0)
|
|
y *= -1.0;
|
|
}
|
|
else if ( nElevationDeg == 90 || nElevationDeg == 270 )
|
|
{
|
|
//sR!=0 cR!=0 cE==0
|
|
//element 12 + 22 --> y=0 or F_PI and x=+-F_PI/2
|
|
//-->element 13/23:
|
|
z = atan(sin(R)/(cos(R)*sin(E)));
|
|
//use element 13 for sign for x
|
|
if( (sin(R)*sin(z))>0.0 )
|
|
x = F_PI/2;
|
|
else
|
|
x = -F_PI/2;
|
|
//use element 21 for y
|
|
if( (sin(R)*sin(E)*sin(z))>0.0)
|
|
y = 0.0;
|
|
else
|
|
y = F_PI;
|
|
}
|
|
else if ( nRotationDeg == 0 || nRotationDeg == 180 )
|
|
{
|
|
//sE!=0 cE!=0 sR==0
|
|
z = 0.0;
|
|
x = E;
|
|
y = R;
|
|
double f23 = cos(R)*sin(E);
|
|
if( (f23 * sin(x)) < 0.0 )
|
|
x *= -1.0; //todo ??
|
|
}
|
|
else if (nRotationDeg == 90 || nRotationDeg == 270)
|
|
{
|
|
//sE!=0 cE!=0 cR==0
|
|
//z = +- F_PI/2;
|
|
//x = +- F_PI/2;
|
|
z = F_PI/2;
|
|
x = F_PI/2;
|
|
double sR = sin(R);
|
|
if( sR<0.0 )
|
|
x *= -1.0; //different signs for x and z
|
|
|
|
//use element 21:
|
|
double cy = sR*sin(E)/sin(z);
|
|
lcl_ensureIntervalMinus1To1(cy);
|
|
y = acos(cy);
|
|
|
|
//use element 22 for sign:
|
|
if( (sin(x)*sin(y)*sin(z)*cos(E))<0.0)
|
|
y *= -1.0;
|
|
}
|
|
else
|
|
{
|
|
z = atan(tan(R) * sin(E));
|
|
if(cos(z)==0.0)
|
|
{
|
|
OSL_FAIL("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
|
|
return;
|
|
}
|
|
double cy = cos(R)/cos(z);
|
|
lcl_ensureIntervalMinus1To1(cy);
|
|
y = acos(cy);
|
|
|
|
//element 12 in 23
|
|
double fDenominator = cos(z)*(1.0-pow(sin(y),2));
|
|
if(fDenominator==0.0)
|
|
{
|
|
OSL_FAIL("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
|
|
return;
|
|
}
|
|
double sx = cos(R)*sin(E)/fDenominator;
|
|
lcl_ensureIntervalMinus1To1(sx);
|
|
x = asin( sx );
|
|
|
|
//use element 13 for sign:
|
|
double f13a = cos(x)*cos(z)*sin(y);
|
|
double f13b = sin(R)-sx*sin(z);
|
|
if( (f13b*f13a)<0.0 )
|
|
{
|
|
//change x or y
|
|
//use element 22 for further investigations:
|
|
//try
|
|
y *= -1;
|
|
double f22a = cos(x)*cos(z);
|
|
double f22b = cos(E)-(sx*sin(y)*sin(z));
|
|
if( (f22a*f22b)<0.0 )
|
|
{
|
|
y *= -1;
|
|
x=(F_PI-x);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//change nothing or both
|
|
//use element 22 for further investigations:
|
|
double f22a = cos(x)*cos(z);
|
|
double f22b = cos(E)-(sx*sin(y)*sin(z));
|
|
if( (f22a*f22b)<0.0 )
|
|
{
|
|
y *= -1;
|
|
x=(F_PI-x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ThreeDHelper::convertXYZAngleRadToElevationRotationDeg(
|
|
sal_Int32& rnElevationDeg, sal_Int32& rnRotationDeg,
|
|
double fXRad, double fYRad, double fZRad)
|
|
{
|
|
// for a description of the algorithm see issue 72994
|
|
//http://www.openoffice.org/issues/show_bug.cgi?id=72994
|
|
//http://www.openoffice.org/nonav/issues/showattachment.cgi/50608/DescriptionCorrected.odt
|
|
|
|
double R = 0.0; //Rotation in Rad
|
|
double E = 0.0; //Elevation in Rad
|
|
|
|
double& x = fXRad;
|
|
double& y = fYRad;
|
|
double& z = fZRad;
|
|
|
|
double f11 = cos(y)*cos(z);
|
|
|
|
if( lcl_isSinZero(y) )
|
|
{
|
|
//siny == 0
|
|
|
|
if( lcl_isCosZero(x) )
|
|
{
|
|
//siny == 0 && cosx == 0
|
|
|
|
if( lcl_isSinZero(z) )
|
|
{
|
|
//siny == 0 && cosx == 0 && sinz == 0
|
|
//example: x=+-90 y=0oder180 z=0(oder180)
|
|
|
|
//element 13+11
|
|
if( f11 > 0 )
|
|
R = 0.0;
|
|
else
|
|
R = F_PI;
|
|
|
|
//element 23
|
|
double f23 = cos(z)*sin(x) / cos(R);
|
|
if( f23 > 0 )
|
|
E = F_PI/2.0;
|
|
else
|
|
E = -F_PI/2.0;
|
|
}
|
|
else if( lcl_isCosZero(z) )
|
|
{
|
|
//siny == 0 && cosx == 0 && cosz == 0
|
|
//example: x=+-90 y=0oder180 z=+-90
|
|
|
|
double f13 = sin(x)*sin(z);
|
|
//element 13+11
|
|
if( f13 > 0 )
|
|
R = F_PI/2.0;
|
|
else
|
|
R = -F_PI/2.0;
|
|
|
|
//element 21
|
|
double f21 = cos(y)*sin(z) / sin(R);
|
|
if( f21 > 0 )
|
|
E = F_PI/2.0;
|
|
else
|
|
E = -F_PI/2.0;
|
|
}
|
|
else
|
|
{
|
|
//siny == 0 && cosx == 0 && cosz != 0 && sinz != 0
|
|
//element 11 && 13
|
|
double f13 = sin(x)*sin(z);
|
|
R = atan( f13/f11 );
|
|
|
|
if(f11<0)
|
|
R+=F_PI;
|
|
|
|
//element 23
|
|
double f23 = cos(z)*sin(x);
|
|
if( f23/cos(R) > 0 )
|
|
E = F_PI/2.0;
|
|
else
|
|
E = -F_PI/2.0;
|
|
}
|
|
}
|
|
else if( lcl_isSinZero(x) )
|
|
{
|
|
//sinY==0 sinX==0
|
|
//element 13+11
|
|
if( f11 > 0 )
|
|
R = 0.0;
|
|
else
|
|
R = F_PI;
|
|
|
|
double f22 = cos(x)*cos(z);
|
|
if( f22 > 0 )
|
|
E = 0.0;
|
|
else
|
|
E = F_PI;
|
|
}
|
|
else if( lcl_isSinZero(z) )
|
|
{
|
|
//sinY==0 sinZ==0 sinx!=0 cosx!=0
|
|
//element 13+11
|
|
if( f11 > 0 )
|
|
R = 0.0;
|
|
else
|
|
R = F_PI;
|
|
|
|
//element 22 && 23
|
|
double f22 = cos(x)*cos(z);
|
|
double f23 = cos(z)*sin(x);
|
|
E = atan( f23/(f22*cos(R)) );
|
|
if( (f22*cos(E))<0 )
|
|
E+=F_PI;
|
|
}
|
|
else if( lcl_isCosZero(z) )
|
|
{
|
|
//sinY == 0 && cosZ == 0 && cosx != 0 && sinx != 0
|
|
double f13 = sin(x)*sin(z);
|
|
//element 13+11
|
|
if( f13 > 0 )
|
|
R = F_PI/2.0;
|
|
else
|
|
R = -F_PI/2.0;
|
|
|
|
//element 21+22
|
|
double f21 = cos(y)*sin(z);
|
|
if( f21/sin(R) > 0 )
|
|
E = F_PI/2.0;
|
|
else
|
|
E = -F_PI/2.0;
|
|
}
|
|
else
|
|
{
|
|
//sinY == 0 && all other !=0
|
|
double f13 = sin(x)*sin(z);
|
|
R = atan( f13/f11 );
|
|
if( (f11*cos(R))<0.0 )
|
|
R+=F_PI;
|
|
|
|
double f22 = cos(x)*cos(z);
|
|
if( !lcl_isCosZero(R) )
|
|
E = atan( cos(z)*sin(x) /( f22*cos(R) ) );
|
|
else
|
|
E = atan( cos(y)*sin(z) /( f22*sin(R) ) );
|
|
if( (f22*cos(E))<0 )
|
|
E+=F_PI;
|
|
}
|
|
}
|
|
else if( lcl_isCosZero(y) )
|
|
{
|
|
//cosY==0
|
|
|
|
double f13 = sin(x)*sin(z)+cos(x)*cos(z)*sin(y);
|
|
if( f13 >= 0 )
|
|
R = F_PI/2.0;
|
|
else
|
|
R = -F_PI/2.0;
|
|
|
|
double f22 = cos(x)*cos(z)+sin(x)*sin(y)*sin(z);
|
|
if( f22 >= 0 )
|
|
E = 0.0;
|
|
else
|
|
E = F_PI;
|
|
}
|
|
else if( lcl_isSinZero(x) )
|
|
{
|
|
//cosY!=0 sinY!=0 sinX=0
|
|
if( lcl_isSinZero(z) )
|
|
{
|
|
//cosY!=0 sinY!=0 sinX=0 sinZ=0
|
|
double f13 = cos(x)*cos(z)*sin(y);
|
|
R = atan( f13/f11 );
|
|
//R = asin(f13);
|
|
if( f11<0 )
|
|
R+=F_PI;
|
|
|
|
double f22 = cos(x)*cos(z);
|
|
if( f22>0 )
|
|
E = 0.0;
|
|
else
|
|
E = F_PI;
|
|
}
|
|
else if( lcl_isCosZero(z) )
|
|
{
|
|
//cosY!=0 sinY!=0 sinX=0 cosZ=0
|
|
R = x;
|
|
E = y;//or -y
|
|
//use 23 for 'signs'
|
|
double f23 = -1.0*cos(x)*sin(y)*sin(z);
|
|
if( (f23*cos(R)*sin(E))<0.0 )
|
|
{
|
|
//change R or E
|
|
E = -y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//cosY!=0 sinY!=0 sinX=0 sinZ!=0 cosZ!=0
|
|
double f13 = cos(x)*cos(z)*sin(y);
|
|
R = atan( f13/f11 );
|
|
|
|
if( f11<0 )
|
|
R+=F_PI;
|
|
|
|
double f21 = cos(y)*sin(z);
|
|
double f22 = cos(x)*cos(z);
|
|
E = atan(f21/(f22*sin(R)) );
|
|
|
|
if( (f22*cos(E))<0.0 )
|
|
E+=F_PI;
|
|
}
|
|
}
|
|
else if( lcl_isCosZero(x) )
|
|
{
|
|
//cosY!=0 sinY!=0 cosX=0
|
|
|
|
if( lcl_isSinZero(z) )
|
|
{
|
|
//cosY!=0 sinY!=0 cosX=0 sinZ=0
|
|
R=0;//13 -> R=0 or F_PI
|
|
if( f11<0.0 )
|
|
R=F_PI;
|
|
E=F_PI/2;//22 -> E=+-F_PI/2
|
|
//use element 11 and 23 for sign
|
|
double f23 = cos(z)*sin(x);
|
|
if( (f11*f23*sin(E))<0.0 )
|
|
E=-F_PI/2.0;
|
|
}
|
|
else if( lcl_isCosZero(z) )
|
|
{
|
|
//cosY!=0 sinY!=0 cosX=0 cosZ=0
|
|
//element 11 & 13:
|
|
if( (sin(x)*sin(z))>0.0 )
|
|
R=F_PI/2.0;
|
|
else
|
|
R=-F_PI/2.0;
|
|
//element 22:
|
|
E=acos( sin(x)*sin(y)*sin(z));
|
|
//use element 21 for sign:
|
|
if( (cos(y)*sin(z)*sin(R)*sin(E))<0.0 )
|
|
E*=-1.0;
|
|
}
|
|
else
|
|
{
|
|
//cosY!=0 sinY!=0 cosX=0 sinZ!=0 cosZ!=0
|
|
//element 13/11
|
|
R = atan( sin(x)*sin(z)/(cos(y)*cos(z)) );
|
|
//use 13 for 'sign'
|
|
if( (sin(x)*sin(z))<0.0 )
|
|
R += F_PI;
|
|
//element 22
|
|
E = acos(sin(x)*sin(y)*sin(z) );
|
|
//use 21 for sign
|
|
if( (cos(y)*sin(z)*sin(R)*sin(E))<0.0 )
|
|
E*=-1.0;
|
|
}
|
|
}
|
|
else if( lcl_isSinZero(z) )
|
|
{
|
|
//cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ=0
|
|
//element 11
|
|
R=y;
|
|
//use elenment 13 for sign
|
|
if( (cos(x)*cos(z)*sin(y)*sin(R))<0.0 )
|
|
R*=-1.0;
|
|
//element 22
|
|
E = acos( cos(x)*cos(z) );
|
|
//use element 23 for sign
|
|
if( (cos(z)*sin(x)*cos(R)*sin(E))<0.0 )
|
|
E*=-1.0;
|
|
}
|
|
else if( lcl_isCosZero(z) )
|
|
{
|
|
//cosY!=0 sinY!=0 sinX!=0 cosX!=0 cosZ=0
|
|
//element 21/23
|
|
R=atan(-cos(y)/(cos(x)*sin(y)));
|
|
//use element 13 for 'sign'
|
|
if( (sin(x)*sin(z)*sin(R))<0.0 )
|
|
R+=F_PI;
|
|
//element 21/22
|
|
E=atan( cos(y)*sin(z)/(sin(R)*sin(x)*sin(y)*sin(z)) );
|
|
//use element 23 for 'sign'
|
|
if( (-cos(x)*sin(y)*sin(z)*cos(R)*sin(E))<0.0 )
|
|
E+=F_PI;
|
|
}
|
|
else
|
|
{
|
|
//cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ!=0 cosZ!=0
|
|
//13/11:
|
|
double f13 = sin(x)*sin(z)+cos(x)*cos(z)*sin(y);
|
|
R = atan( f13/ f11 );
|
|
if(f11<0.0)
|
|
R+=F_PI;
|
|
double f22 = cos(x)*cos(z)+sin(x)*sin(y)*sin(z);
|
|
double f23 = cos(x)*sin(y)*sin(z)-cos(z)*sin(x);
|
|
//23/22:
|
|
E = atan( -1.0*f23/(f22*cos(R)) );
|
|
if(f22<0.0)
|
|
E+=F_PI;
|
|
}
|
|
|
|
rnElevationDeg = ::basegfx::fround( BaseGFXHelper::Rad2Deg( E ) );
|
|
rnRotationDeg = ::basegfx::fround( BaseGFXHelper::Rad2Deg( R ) );
|
|
}
|
|
|
|
double ThreeDHelper::getValueClippedToRange( double fAngle, const double& fPositivLimit )
|
|
{
|
|
if( fAngle<-1*fPositivLimit )
|
|
fAngle=-1*fPositivLimit;
|
|
else if( fAngle>fPositivLimit )
|
|
fAngle=fPositivLimit;
|
|
return fAngle;
|
|
}
|
|
|
|
double ThreeDHelper::getXDegreeAngleLimitForRightAngledAxes()
|
|
{
|
|
return 90.0;
|
|
}
|
|
|
|
double ThreeDHelper::getYDegreeAngleLimitForRightAngledAxes()
|
|
{
|
|
return 45.0;
|
|
}
|
|
|
|
void ThreeDHelper::adaptRadAnglesForRightAngledAxes( double& rfXAngleRad, double& rfYAngleRad )
|
|
{
|
|
rfXAngleRad = ThreeDHelper::getValueClippedToRange(rfXAngleRad, BaseGFXHelper::Deg2Rad(ThreeDHelper::getXDegreeAngleLimitForRightAngledAxes()) );
|
|
rfYAngleRad = ThreeDHelper::getValueClippedToRange(rfYAngleRad, BaseGFXHelper::Deg2Rad(ThreeDHelper::getYDegreeAngleLimitForRightAngledAxes()) );
|
|
}
|
|
|
|
void ThreeDHelper::getRotationAngleFromDiagram(
|
|
const Reference< beans::XPropertySet >& xSceneProperties, double& rfXAngleRad, double& rfYAngleRad, double& rfZAngleRad )
|
|
{
|
|
//takes the camera and the transformation matrix into account
|
|
|
|
rfXAngleRad = rfYAngleRad = rfZAngleRad = 0.0;
|
|
|
|
if( !xSceneProperties.is() )
|
|
return;
|
|
|
|
//get camera rotation
|
|
::basegfx::B3DHomMatrix aFixCameraRotationMatrix( lcl_getCameraMatrix( xSceneProperties ) );
|
|
BaseGFXHelper::ReduceToRotationMatrix( aFixCameraRotationMatrix );
|
|
|
|
//get scene rotation
|
|
::basegfx::B3DHomMatrix aSceneRotation;
|
|
{
|
|
drawing::HomogenMatrix aHomMatrix;
|
|
if( xSceneProperties->getPropertyValue( "D3DTransformMatrix") >>= aHomMatrix )
|
|
{
|
|
aSceneRotation = BaseGFXHelper::HomogenMatrixToB3DHomMatrix( aHomMatrix );
|
|
BaseGFXHelper::ReduceToRotationMatrix( aSceneRotation );
|
|
}
|
|
}
|
|
|
|
::basegfx::B3DHomMatrix aResultRotation = aFixCameraRotationMatrix * aSceneRotation;
|
|
::basegfx::B3DTuple aRotation( BaseGFXHelper::GetRotationFromMatrix( aResultRotation ) );
|
|
|
|
rfXAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(aRotation.getX());
|
|
rfYAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(aRotation.getY());
|
|
rfZAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(aRotation.getZ());
|
|
|
|
if(rfZAngleRad<(-F_PI/2) || rfZAngleRad>(F_PI/2))
|
|
{
|
|
rfZAngleRad-=F_PI;
|
|
rfXAngleRad-=F_PI;
|
|
rfYAngleRad=(F_PI-rfYAngleRad);
|
|
|
|
rfXAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(rfXAngleRad);
|
|
rfYAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(rfYAngleRad);
|
|
rfZAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(rfZAngleRad);
|
|
}
|
|
}
|
|
|
|
void ThreeDHelper::switchRightAngledAxes( const Reference< beans::XPropertySet >& xSceneProperties, sal_Bool bRightAngledAxes, bool bRotateLights )
|
|
{
|
|
try
|
|
{
|
|
if( xSceneProperties.is() )
|
|
{
|
|
sal_Bool bOldRightAngledAxes = sal_False;
|
|
xSceneProperties->getPropertyValue( "RightAngledAxes") >>= bOldRightAngledAxes;
|
|
if( bOldRightAngledAxes!=bRightAngledAxes)
|
|
{
|
|
xSceneProperties->setPropertyValue( "RightAngledAxes", uno::makeAny( bRightAngledAxes ));
|
|
if( bRotateLights )
|
|
{
|
|
if(bRightAngledAxes)
|
|
{
|
|
::basegfx::B3DHomMatrix aInverseRotation( lcl_getInverseRotationMatrix( xSceneProperties ) );
|
|
lcl_rotateLights( aInverseRotation, xSceneProperties );
|
|
}
|
|
else
|
|
{
|
|
::basegfx::B3DHomMatrix aCompleteRotation( lcl_getCompleteRotationMatrix( xSceneProperties ) );
|
|
lcl_rotateLights( aCompleteRotation, xSceneProperties );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch( const uno::Exception & ex )
|
|
{
|
|
ASSERT_EXCEPTION( ex );
|
|
}
|
|
}
|
|
|
|
void ThreeDHelper::setRotationAngleToDiagram(
|
|
const Reference< beans::XPropertySet >& xSceneProperties
|
|
, double fXAngleRad, double fYAngleRad, double fZAngleRad )
|
|
{
|
|
//the rotation of the camera is not touched but taken into account
|
|
//the rotation difference is applied to the transformation matrix
|
|
|
|
//the light sources will be adapted also
|
|
|
|
if( !xSceneProperties.is() )
|
|
return;
|
|
|
|
try
|
|
{
|
|
//remind old rotation for adaption of light directions
|
|
::basegfx::B3DHomMatrix aInverseOldRotation( lcl_getInverseRotationMatrix( xSceneProperties ) );
|
|
|
|
::basegfx::B3DHomMatrix aInverseCameraRotation;
|
|
{
|
|
::basegfx::B3DTuple aR( BaseGFXHelper::GetRotationFromMatrix(
|
|
lcl_getCameraMatrix( xSceneProperties ) ) );
|
|
aInverseCameraRotation.rotate( 0.0, 0.0, -aR.getZ() );
|
|
aInverseCameraRotation.rotate( 0.0, -aR.getY(), 0.0 );
|
|
aInverseCameraRotation.rotate( -aR.getX(), 0.0, 0.0 );
|
|
}
|
|
|
|
::basegfx::B3DHomMatrix aCumulatedRotation;
|
|
aCumulatedRotation.rotate( fXAngleRad, fYAngleRad, fZAngleRad );
|
|
|
|
//calculate new scene matrix
|
|
::basegfx::B3DHomMatrix aSceneRotation = aInverseCameraRotation*aCumulatedRotation;
|
|
BaseGFXHelper::ReduceToRotationMatrix( aSceneRotation );
|
|
|
|
//set new rotation to transformation matrix
|
|
xSceneProperties->setPropertyValue(
|
|
"D3DTransformMatrix", uno::makeAny( BaseGFXHelper::B3DHomMatrixToHomogenMatrix( aSceneRotation )));
|
|
|
|
//rotate lights if RightAngledAxes are not set or not supported
|
|
sal_Bool bRightAngledAxes = sal_False;
|
|
xSceneProperties->getPropertyValue( "RightAngledAxes") >>= bRightAngledAxes;
|
|
uno::Reference< chart2::XDiagram > xDiagram( xSceneProperties, uno::UNO_QUERY );
|
|
if(!bRightAngledAxes || !ChartTypeHelper::isSupportingRightAngledAxes(
|
|
DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) ) )
|
|
{
|
|
::basegfx::B3DHomMatrix aNewRotation;
|
|
aNewRotation.rotate( fXAngleRad, fYAngleRad, fZAngleRad );
|
|
lcl_rotateLights( aNewRotation*aInverseOldRotation, xSceneProperties );
|
|
}
|
|
}
|
|
catch( const uno::Exception & ex )
|
|
{
|
|
ASSERT_EXCEPTION( ex );
|
|
}
|
|
}
|
|
|
|
void ThreeDHelper::getRotationFromDiagram( const uno::Reference< beans::XPropertySet >& xSceneProperties
|
|
, sal_Int32& rnHorizontalAngleDegree, sal_Int32& rnVerticalAngleDegree )
|
|
{
|
|
double fXAngle, fYAngle, fZAngle;
|
|
ThreeDHelper::getRotationAngleFromDiagram( xSceneProperties, fXAngle, fYAngle, fZAngle );
|
|
|
|
if( !lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
|
|
{
|
|
ThreeDHelper::convertXYZAngleRadToElevationRotationDeg(
|
|
rnHorizontalAngleDegree, rnVerticalAngleDegree, fXAngle, fYAngle, fZAngle);
|
|
rnVerticalAngleDegree*=-1;
|
|
}
|
|
else
|
|
{
|
|
fXAngle = BaseGFXHelper::Rad2Deg( fXAngle );
|
|
fYAngle = BaseGFXHelper::Rad2Deg( fYAngle );
|
|
fZAngle = BaseGFXHelper::Rad2Deg( fZAngle );
|
|
|
|
rnHorizontalAngleDegree = ::basegfx::fround(fXAngle);
|
|
rnVerticalAngleDegree = ::basegfx::fround(-1.0*fYAngle);
|
|
//nZRotation = ::basegfx::fround(-1.0*fZAngle);
|
|
}
|
|
|
|
lcl_shiftAngleToIntervalMinus180To180( rnHorizontalAngleDegree );
|
|
lcl_shiftAngleToIntervalMinus180To180( rnVerticalAngleDegree );
|
|
}
|
|
|
|
void ThreeDHelper::setRotationToDiagram( const uno::Reference< beans::XPropertySet >& xSceneProperties
|
|
, sal_Int32 nHorizontalAngleDegree, sal_Int32 nVerticalYAngleDegree )
|
|
{
|
|
//todo: x and y is not equal to horz and vert in case of RightAngledAxes==false
|
|
double fXAngle = BaseGFXHelper::Deg2Rad( nHorizontalAngleDegree );
|
|
double fYAngle = BaseGFXHelper::Deg2Rad( -1*nVerticalYAngleDegree );
|
|
double fZAngle = 0.0;
|
|
|
|
if( !lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
|
|
ThreeDHelper::convertElevationRotationDegToXYZAngleRad(
|
|
nHorizontalAngleDegree, -1*nVerticalYAngleDegree, fXAngle, fYAngle, fZAngle );
|
|
|
|
ThreeDHelper::setRotationAngleToDiagram( xSceneProperties, fXAngle, fYAngle, fZAngle );
|
|
}
|
|
|
|
void ThreeDHelper::getCameraDistanceRange( double& rfMinimumDistance, double& rfMaximumDistance )
|
|
{
|
|
rfMinimumDistance = 3.0/4.0*FIXED_SIZE_FOR_3D_CHART_VOLUME;//empiric value
|
|
rfMaximumDistance = 20.0*FIXED_SIZE_FOR_3D_CHART_VOLUME;//empiric value
|
|
}
|
|
|
|
void ThreeDHelper::ensureCameraDistanceRange( double& rfCameraDistance )
|
|
{
|
|
double fMin, fMax;
|
|
getCameraDistanceRange( fMin, fMax );
|
|
if( rfCameraDistance < fMin )
|
|
rfCameraDistance = fMin;
|
|
if( rfCameraDistance > fMax )
|
|
rfCameraDistance = fMax;
|
|
}
|
|
|
|
double ThreeDHelper::getCameraDistance(
|
|
const Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
double fCameraDistance = FIXED_SIZE_FOR_3D_CHART_VOLUME;
|
|
|
|
if( !xSceneProperties.is() )
|
|
return fCameraDistance;
|
|
|
|
try
|
|
{
|
|
drawing::CameraGeometry aCG( ThreeDHelper::getDefaultCameraGeometry() );
|
|
xSceneProperties->getPropertyValue( "D3DCameraGeometry" ) >>= aCG;
|
|
::basegfx::B3DVector aVRP( BaseGFXHelper::Position3DToB3DVector( aCG.vrp ) );
|
|
fCameraDistance = aVRP.getLength();
|
|
|
|
ensureCameraDistanceRange( fCameraDistance );
|
|
}
|
|
catch( const uno::Exception & ex )
|
|
{
|
|
ASSERT_EXCEPTION( ex );
|
|
}
|
|
return fCameraDistance;
|
|
}
|
|
|
|
void ThreeDHelper::setCameraDistance(
|
|
const Reference< beans::XPropertySet >& xSceneProperties, double fCameraDistance )
|
|
{
|
|
if( !xSceneProperties.is() )
|
|
return;
|
|
|
|
try
|
|
{
|
|
if( fCameraDistance <= 0 )
|
|
fCameraDistance = FIXED_SIZE_FOR_3D_CHART_VOLUME;
|
|
|
|
drawing::CameraGeometry aCG( ThreeDHelper::getDefaultCameraGeometry() );
|
|
xSceneProperties->getPropertyValue( "D3DCameraGeometry" ) >>= aCG;
|
|
::basegfx::B3DVector aVRP( BaseGFXHelper::Position3DToB3DVector( aCG.vrp ) );
|
|
if( ::basegfx::fTools::equalZero( aVRP.getLength() ) )
|
|
aVRP = ::basegfx::B3DVector(0,0,1);
|
|
aVRP.setLength(fCameraDistance);
|
|
aCG.vrp = BaseGFXHelper::B3DVectorToPosition3D( aVRP );
|
|
|
|
xSceneProperties->setPropertyValue( "D3DCameraGeometry", uno::makeAny( aCG ));
|
|
}
|
|
catch( const uno::Exception & ex )
|
|
{
|
|
ASSERT_EXCEPTION( ex );
|
|
}
|
|
}
|
|
|
|
double ThreeDHelper::CameraDistanceToPerspective( double fCameraDistance )
|
|
{
|
|
double fMin, fMax;
|
|
ThreeDHelper::getCameraDistanceRange( fMin, fMax );
|
|
//fMax <-> 0; fMin <->100
|
|
//a/x + b = y
|
|
double a = 100.0*fMax*fMin/(fMax-fMin);
|
|
double b = -a/fMax;
|
|
|
|
double fRet = a/fCameraDistance + b;
|
|
|
|
return fRet;
|
|
}
|
|
|
|
double ThreeDHelper::PerspectiveToCameraDistance( double fPerspective )
|
|
{
|
|
double fMin, fMax;
|
|
ThreeDHelper::getCameraDistanceRange( fMin, fMax );
|
|
//fMax <-> 0; fMin <->100
|
|
//a/x + b = y
|
|
double a = 100.0*fMax*fMin/(fMax-fMin);
|
|
double b = -a/fMax;
|
|
|
|
double fRet = a/(fPerspective - b);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
ThreeDLookScheme ThreeDHelper::detectScheme( const uno::Reference< XDiagram >& xDiagram )
|
|
{
|
|
ThreeDLookScheme aScheme = ThreeDLookScheme_Unknown;
|
|
|
|
sal_Int32 nRoundedEdges;
|
|
sal_Int32 nObjectLines;
|
|
ThreeDHelper::getRoundedEdgesAndObjectLines( xDiagram, nRoundedEdges, nObjectLines );
|
|
|
|
//get shade mode and light settings:
|
|
drawing::ShadeMode aShadeMode( drawing::ShadeMode_SMOOTH );
|
|
uno::Reference< beans::XPropertySet > xDiagramProps( xDiagram, uno::UNO_QUERY );
|
|
try
|
|
{
|
|
if( xDiagramProps.is() )
|
|
xDiagramProps->getPropertyValue( "D3DSceneShadeMode" )>>= aShadeMode;
|
|
}
|
|
catch( const uno::Exception & ex )
|
|
{
|
|
ASSERT_EXCEPTION( ex );
|
|
}
|
|
|
|
if( lcl_isSimpleScheme( aShadeMode, nRoundedEdges, nObjectLines, xDiagram ) )
|
|
{
|
|
if( lcl_isSimpleLightScheme(xDiagramProps) )
|
|
aScheme = ThreeDLookScheme_Simple;
|
|
}
|
|
else if( lcl_isRealisticScheme( aShadeMode, nRoundedEdges, nObjectLines ) )
|
|
{
|
|
if( lcl_isRealisticLightScheme(xDiagramProps) )
|
|
aScheme = ThreeDLookScheme_Realistic;
|
|
}
|
|
|
|
return aScheme;
|
|
}
|
|
|
|
void ThreeDHelper::setScheme( const uno::Reference< XDiagram >& xDiagram, ThreeDLookScheme aScheme )
|
|
{
|
|
if( aScheme == ThreeDLookScheme_Unknown )
|
|
return;
|
|
|
|
drawing::ShadeMode aShadeMode;
|
|
sal_Int32 nRoundedEdges;
|
|
sal_Int32 nObjectLines;
|
|
|
|
if( aScheme == ThreeDLookScheme_Simple )
|
|
lcl_setSimpleScheme(aShadeMode,nRoundedEdges,nObjectLines,xDiagram);
|
|
else
|
|
lcl_setRealisticScheme(aShadeMode,nRoundedEdges,nObjectLines);
|
|
|
|
try
|
|
{
|
|
ThreeDHelper::setRoundedEdgesAndObjectLines( xDiagram, nRoundedEdges, nObjectLines );
|
|
|
|
uno::Reference< beans::XPropertySet > xProp( xDiagram, uno::UNO_QUERY );
|
|
if( xProp.is() )
|
|
{
|
|
drawing::ShadeMode aOldShadeMode;
|
|
if( ! ( (xProp->getPropertyValue( "D3DSceneShadeMode" )>>=aOldShadeMode) &&
|
|
aOldShadeMode == aShadeMode ))
|
|
{
|
|
xProp->setPropertyValue( "D3DSceneShadeMode", uno::makeAny( aShadeMode ));
|
|
}
|
|
}
|
|
|
|
lcl_setLightsForScheme( xProp, aScheme );
|
|
}
|
|
catch( const uno::Exception & ex )
|
|
{
|
|
ASSERT_EXCEPTION( ex );
|
|
}
|
|
|
|
}
|
|
|
|
void ThreeDHelper::set3DSettingsToDefault( const uno::Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
Reference< beans::XPropertyState > xState( xSceneProperties, uno::UNO_QUERY );
|
|
if(xState.is())
|
|
{
|
|
xState->setPropertyToDefault( "D3DSceneDistance");
|
|
xState->setPropertyToDefault( "D3DSceneFocalLength");
|
|
}
|
|
ThreeDHelper::setDefaultRotation( xSceneProperties );
|
|
ThreeDHelper::setDefaultIllumination( xSceneProperties );
|
|
}
|
|
|
|
void ThreeDHelper::setDefaultRotation( const uno::Reference< beans::XPropertySet >& xSceneProperties, bool bPieOrDonut )
|
|
{
|
|
if( !xSceneProperties.is() )
|
|
return;
|
|
|
|
drawing::CameraGeometry aCameraGeo( ThreeDHelper::getDefaultCameraGeometry( bPieOrDonut ) );
|
|
xSceneProperties->setPropertyValue( "D3DCameraGeometry", uno::makeAny( aCameraGeo ));
|
|
|
|
::basegfx::B3DHomMatrix aSceneRotation;
|
|
if( bPieOrDonut )
|
|
aSceneRotation.rotate( -F_PI/3.0, 0, 0 );
|
|
xSceneProperties->setPropertyValue( "D3DTransformMatrix",
|
|
uno::makeAny( BaseGFXHelper::B3DHomMatrixToHomogenMatrix( aSceneRotation )));
|
|
}
|
|
|
|
void ThreeDHelper::setDefaultRotation( const uno::Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
bool bPieOrDonut( DiagramHelper::isPieOrDonutChart( uno::Reference< XDiagram >(xSceneProperties, uno::UNO_QUERY) ) );
|
|
ThreeDHelper::setDefaultRotation( xSceneProperties, bPieOrDonut );
|
|
}
|
|
|
|
void ThreeDHelper::setDefaultIllumination( const uno::Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
if( !xSceneProperties.is() )
|
|
return;
|
|
|
|
drawing::ShadeMode aShadeMode( drawing::ShadeMode_SMOOTH );
|
|
try
|
|
{
|
|
xSceneProperties->getPropertyValue( "D3DSceneShadeMode" )>>= aShadeMode;
|
|
xSceneProperties->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_1, uno::makeAny( sal_False ) );
|
|
xSceneProperties->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_3, uno::makeAny( sal_False ) );
|
|
xSceneProperties->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_4, uno::makeAny( sal_False ) );
|
|
xSceneProperties->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_5, uno::makeAny( sal_False ) );
|
|
xSceneProperties->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_6, uno::makeAny( sal_False ) );
|
|
xSceneProperties->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_7, uno::makeAny( sal_False ) );
|
|
xSceneProperties->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_8, uno::makeAny( sal_False ) );
|
|
}
|
|
catch( const uno::Exception & ex )
|
|
{
|
|
ASSERT_EXCEPTION( ex );
|
|
}
|
|
|
|
ThreeDLookScheme aScheme = (drawing::ShadeMode_FLAT==aShadeMode) ? ThreeDLookScheme_Simple : ThreeDLookScheme_Realistic;
|
|
lcl_setLightsForScheme( xSceneProperties, aScheme );
|
|
}
|
|
|
|
void ThreeDHelper::getRoundedEdgesAndObjectLines(
|
|
const uno::Reference< XDiagram > & xDiagram
|
|
, sal_Int32& rnRoundedEdges, sal_Int32& rnObjectLines )
|
|
{
|
|
rnRoundedEdges = -1;
|
|
rnObjectLines = -1;
|
|
try
|
|
{
|
|
bool bDifferentRoundedEdges = false;
|
|
bool bDifferentObjectLines = false;
|
|
|
|
drawing::LineStyle aLineStyle( drawing::LineStyle_SOLID );
|
|
|
|
::std::vector< uno::Reference< XDataSeries > > aSeriesList(
|
|
DiagramHelper::getDataSeriesFromDiagram( xDiagram ) );
|
|
sal_Int32 nSeriesCount = static_cast<sal_Int32>( aSeriesList.size() );
|
|
|
|
OUString aPercentDiagonalPropertyName( "PercentDiagonal" );
|
|
OUString aBorderStylePropertyName( "BorderStyle" );
|
|
|
|
for( sal_Int32 nS = 0; nS < nSeriesCount; ++nS )
|
|
{
|
|
uno::Reference< XDataSeries > xSeries( aSeriesList[nS] );
|
|
uno::Reference< beans::XPropertySet > xProp( xSeries, uno::UNO_QUERY );
|
|
if(!nS)
|
|
{
|
|
rnRoundedEdges = 0;
|
|
try
|
|
{
|
|
sal_Int16 nPercentDiagonal = 0;
|
|
|
|
xProp->getPropertyValue( aPercentDiagonalPropertyName ) >>= nPercentDiagonal;
|
|
rnRoundedEdges = static_cast< sal_Int32 >( nPercentDiagonal );
|
|
|
|
if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
|
|
, aPercentDiagonalPropertyName, uno::makeAny(nPercentDiagonal) ) )
|
|
bDifferentRoundedEdges = true;
|
|
}
|
|
catch( const uno::Exception& e )
|
|
{
|
|
ASSERT_EXCEPTION( e );
|
|
bDifferentRoundedEdges = true;
|
|
}
|
|
try
|
|
{
|
|
xProp->getPropertyValue( aBorderStylePropertyName ) >>= aLineStyle;
|
|
|
|
if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
|
|
, aBorderStylePropertyName, uno::makeAny(aLineStyle) ) )
|
|
bDifferentObjectLines = true;
|
|
}
|
|
catch( const uno::Exception& e )
|
|
{
|
|
ASSERT_EXCEPTION( e );
|
|
bDifferentObjectLines = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( !bDifferentRoundedEdges )
|
|
{
|
|
sal_Int16 nPercentDiagonal = 0;
|
|
xProp->getPropertyValue( aPercentDiagonalPropertyName ) >>= nPercentDiagonal;
|
|
sal_Int32 nCurrentRoundedEdges = static_cast< sal_Int32 >( nPercentDiagonal );
|
|
if(nCurrentRoundedEdges!=rnRoundedEdges
|
|
|| DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
|
|
, aPercentDiagonalPropertyName, uno::makeAny( static_cast< sal_Int16 >(rnRoundedEdges) ) ) )
|
|
{
|
|
bDifferentRoundedEdges = true;
|
|
nCurrentRoundedEdges = -1;
|
|
}
|
|
}
|
|
|
|
if( !bDifferentObjectLines )
|
|
{
|
|
drawing::LineStyle aCurrentLineStyle;
|
|
xProp->getPropertyValue( aBorderStylePropertyName ) >>= aCurrentLineStyle;
|
|
if(aCurrentLineStyle!=aLineStyle
|
|
|| DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
|
|
, aBorderStylePropertyName, uno::makeAny(aLineStyle) ) )
|
|
bDifferentObjectLines = true;
|
|
}
|
|
}
|
|
if( bDifferentRoundedEdges && bDifferentObjectLines )
|
|
break;
|
|
}
|
|
|
|
//set rnObjectLines
|
|
rnObjectLines = 0;
|
|
if( bDifferentObjectLines )
|
|
rnObjectLines = -1;
|
|
else if( aLineStyle == drawing::LineStyle_SOLID )
|
|
rnObjectLines = 1;
|
|
}
|
|
catch( const uno::Exception& e )
|
|
{
|
|
ASSERT_EXCEPTION( e );
|
|
}
|
|
}
|
|
|
|
void ThreeDHelper::setRoundedEdgesAndObjectLines(
|
|
const uno::Reference< XDiagram > & xDiagram
|
|
, sal_Int32 nRoundedEdges, sal_Int32 nObjectLines )
|
|
{
|
|
if( (nRoundedEdges<0||nRoundedEdges>100) && nObjectLines!=0 && nObjectLines!=1 )
|
|
return;
|
|
|
|
drawing::LineStyle aLineStyle( drawing::LineStyle_NONE );
|
|
if(nObjectLines==1)
|
|
aLineStyle = drawing::LineStyle_SOLID;
|
|
|
|
uno::Any aALineStyle( uno::makeAny(aLineStyle));
|
|
uno::Any aARoundedEdges( uno::makeAny( static_cast< sal_Int16 >( nRoundedEdges )));
|
|
|
|
::std::vector< uno::Reference< XDataSeries > > aSeriesList(
|
|
DiagramHelper::getDataSeriesFromDiagram( xDiagram ) );
|
|
sal_Int32 nSeriesCount = static_cast<sal_Int32>( aSeriesList.size() );
|
|
for( sal_Int32 nS = 0; nS < nSeriesCount; ++nS )
|
|
{
|
|
uno::Reference< XDataSeries > xSeries( aSeriesList[nS] );
|
|
|
|
if( nRoundedEdges>=0 && nRoundedEdges<=100 )
|
|
DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries, "PercentDiagonal", aARoundedEdges );
|
|
|
|
if( nObjectLines==0 || nObjectLines==1 )
|
|
DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries, "BorderStyle", aALineStyle );
|
|
}
|
|
}
|
|
|
|
CuboidPlanePosition ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( const Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
CuboidPlanePosition eRet(CuboidPlanePosition_Left);
|
|
|
|
double fXAngleRad=0.0; double fYAngleRad=0.0; double fZAngleRad=0.0;
|
|
ThreeDHelper::getRotationAngleFromDiagram( xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
|
|
if( lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
|
|
{
|
|
ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad, fYAngleRad );
|
|
fZAngleRad=0.0;
|
|
}
|
|
if( sin(fYAngleRad)>0.0 )
|
|
eRet = CuboidPlanePosition_Right;
|
|
return eRet;
|
|
}
|
|
|
|
CuboidPlanePosition ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( const Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
CuboidPlanePosition eRet(CuboidPlanePosition_Back);
|
|
|
|
double fXAngleRad=0.0; double fYAngleRad=0.0; double fZAngleRad=0.0;
|
|
ThreeDHelper::getRotationAngleFromDiagram( xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
|
|
if( lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
|
|
{
|
|
ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad, fYAngleRad );
|
|
fZAngleRad=0.0;
|
|
}
|
|
if( cos(fXAngleRad)*cos(fYAngleRad)<0.0 )
|
|
eRet = CuboidPlanePosition_Front;
|
|
return eRet;
|
|
}
|
|
|
|
CuboidPlanePosition ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( const Reference< beans::XPropertySet >& xSceneProperties )
|
|
{
|
|
CuboidPlanePosition eRet(CuboidPlanePosition_Bottom);
|
|
|
|
double fXAngleRad=0.0; double fYAngleRad=0.0; double fZAngleRad=0.0;
|
|
ThreeDHelper::getRotationAngleFromDiagram( xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
|
|
if( lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
|
|
{
|
|
ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad, fYAngleRad );
|
|
fZAngleRad=0.0;
|
|
}
|
|
if( sin(fXAngleRad)*cos(fYAngleRad)<0.0 )
|
|
eRet = CuboidPlanePosition_Top;
|
|
return eRet;
|
|
}
|
|
|
|
} //namespace chart
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|