diff options
Diffstat (limited to '')
-rw-r--r-- | chart2/source/view/main/PlottingPositionHelper.cxx | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/chart2/source/view/main/PlottingPositionHelper.cxx b/chart2/source/view/main/PlottingPositionHelper.cxx new file mode 100644 index 000000000..eab2f69e7 --- /dev/null +++ b/chart2/source/view/main/PlottingPositionHelper.cxx @@ -0,0 +1,708 @@ +/* -*- 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 <PlottingPositionHelper.hxx> +#include <CommonConverters.hxx> +#include <Linear3DTransformation.hxx> +#include <VPolarTransformation.hxx> +#include <ShapeFactory.hxx> +#include <PropertyMapper.hxx> +#include <defines.hxx> + +#include <com/sun/star/chart/TimeUnit.hpp> +#include <com/sun/star/chart2/AxisType.hpp> +#include <com/sun/star/drawing/Position3D.hpp> + +#include <rtl/math.hxx> + +namespace chart +{ +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + +XTransformation2::~XTransformation2() {} + +PlottingPositionHelper::PlottingPositionHelper() + : m_bSwapXAndY( false ) + , m_nXResolution( 1000 ) + , m_nYResolution( 1000 ) + , m_nZResolution( 1000 ) + , m_bMaySkipPointsInRegressionCalculation( true ) + , m_bDateAxis(false) + , m_nTimeResolution( css::chart::TimeUnit::DAY ) + , m_aNullDate(30,12,1899) + , m_fScaledCategoryWidth(1.0) + , m_bAllowShiftXAxisPos(false) + , m_bAllowShiftZAxisPos(false) +{ +} +PlottingPositionHelper::PlottingPositionHelper( const PlottingPositionHelper& rSource ) + : m_aScales( rSource.m_aScales ) + , m_aMatrixScreenToScene( rSource.m_aMatrixScreenToScene ) + // m_xTransformationLogicToScene( nullptr ) //should be recalculated + , m_bSwapXAndY( rSource.m_bSwapXAndY ) + , m_nXResolution( rSource.m_nXResolution ) + , m_nYResolution( rSource.m_nYResolution ) + , m_nZResolution( rSource.m_nZResolution ) + , m_bMaySkipPointsInRegressionCalculation( rSource.m_bMaySkipPointsInRegressionCalculation ) + , m_bDateAxis( rSource.m_bDateAxis ) + , m_nTimeResolution( rSource.m_nTimeResolution ) + , m_aNullDate( rSource.m_aNullDate ) + , m_fScaledCategoryWidth( rSource.m_fScaledCategoryWidth ) + , m_bAllowShiftXAxisPos( rSource.m_bAllowShiftXAxisPos ) + , m_bAllowShiftZAxisPos( rSource.m_bAllowShiftZAxisPos ) +{ +} + +PlottingPositionHelper::~PlottingPositionHelper() +{ + +} + +std::unique_ptr<PlottingPositionHelper> PlottingPositionHelper::clone() const +{ + return std::make_unique<PlottingPositionHelper>(*this); +} + +std::unique_ptr<PlottingPositionHelper> PlottingPositionHelper::createSecondaryPosHelper( const ExplicitScaleData& rSecondaryScale ) +{ + auto pRet = clone(); + pRet->m_aScales[1]=rSecondaryScale; + return pRet; +} + +void PlottingPositionHelper::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix) +{ + m_aMatrixScreenToScene = HomogenMatrixToB3DHomMatrix(rMatrix); + m_xTransformationLogicToScene = nullptr; +} + +void PlottingPositionHelper::setScales( std::vector< ExplicitScaleData >&& rScales, bool bSwapXAndYAxis ) +{ + m_aScales = std::move(rScales); + m_bSwapXAndY = bSwapXAndYAxis; + m_xTransformationLogicToScene = nullptr; +} + +::chart::XTransformation2* PlottingPositionHelper::getTransformationScaledLogicToScene() const +{ + //this is a standard transformation for a cartesian coordinate system + + //transformation from 2) to 4) //@todo 2) and 4) need an ink to a document + + //we need to apply this transformation to each geometric object because of a bug/problem + //of the old drawing layer (the UNO_NAME_3D_EXTRUDE_DEPTH is an integer value instead of a double ) + if(!m_xTransformationLogicToScene) + { + ::basegfx::B3DHomMatrix aMatrix; + double MinX = getLogicMinX(); + double MinY = getLogicMinY(); + double MinZ = getLogicMinZ(); + double MaxX = getLogicMaxX(); + double MaxY = getLogicMaxY(); + double MaxZ = getLogicMaxZ(); + + AxisOrientation nXAxisOrientation = m_aScales[0].Orientation; + AxisOrientation nYAxisOrientation = m_aScales[1].Orientation; + AxisOrientation nZAxisOrientation = m_aScales[2].Orientation; + + //apply scaling + doUnshiftedLogicScaling( &MinX, &MinY, &MinZ ); + doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ); + + if(m_bSwapXAndY) + { + std::swap(MinX,MinY); + std::swap(MaxX,MaxY); + std::swap(nXAxisOrientation,nYAxisOrientation); + } + + double fWidthX = MaxX - MinX; + double fWidthY = MaxY - MinY; + double fWidthZ = MaxZ - MinZ; + + double fScaleDirectionX = nXAxisOrientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0; + double fScaleDirectionY = nYAxisOrientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0; + double fScaleDirectionZ = nZAxisOrientation==AxisOrientation_MATHEMATICAL ? -1.0 : 1.0; + + double fScaleX = fScaleDirectionX*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthX; + double fScaleY = fScaleDirectionY*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthY; + double fScaleZ = fScaleDirectionZ*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthZ; + + aMatrix.scale(fScaleX, fScaleY, fScaleZ); + + if( nXAxisOrientation==AxisOrientation_MATHEMATICAL ) + aMatrix.translate(-MinX*fScaleX, 0.0, 0.0); + else + aMatrix.translate(-MaxX*fScaleX, 0.0, 0.0); + if( nYAxisOrientation==AxisOrientation_MATHEMATICAL ) + aMatrix.translate(0.0, -MinY*fScaleY, 0.0); + else + aMatrix.translate(0.0, -MaxY*fScaleY, 0.0); + if( nZAxisOrientation==AxisOrientation_MATHEMATICAL ) + aMatrix.translate(0.0, 0.0, -MaxZ*fScaleZ);//z direction in draw is reverse mathematical direction + else + aMatrix.translate(0.0, 0.0, -MinZ*fScaleZ); + + aMatrix = m_aMatrixScreenToScene*aMatrix; + + m_xTransformationLogicToScene.reset(new Linear3DTransformation(B3DHomMatrixToHomogenMatrix( aMatrix ), m_bSwapXAndY)); + } + return m_xTransformationLogicToScene.get(); +} + +drawing::Position3D PlottingPositionHelper::transformLogicToScene( + double fX, double fY, double fZ, bool bClip ) const +{ + doLogicScaling( &fX,&fY,&fZ ); + if(bClip) + clipScaledLogicValues( &fX,&fY,&fZ ); + + return transformScaledLogicToScene( fX, fY, fZ, false ); +} + +drawing::Position3D PlottingPositionHelper::transformScaledLogicToScene( + double fX, double fY, double fZ, bool bClip ) const +{ + if( bClip ) + clipScaledLogicValues( &fX,&fY,&fZ ); + + drawing::Position3D aPos( fX, fY, fZ); + + ::chart::XTransformation2* pTransformation = + getTransformationScaledLogicToScene(); + return pTransformation->transform( aPos ); +} + +awt::Point PlottingPositionHelper::transformSceneToScreenPosition( const drawing::Position3D& rScenePosition3D + , const rtl::Reference<SvxShapeGroupAnyD>& xSceneTarget + , sal_Int32 nDimensionCount ) +{ + //@todo would like to have a cheaper method to do this transformation + awt::Point aScreenPoint( static_cast<sal_Int32>(rScenePosition3D.PositionX), static_cast<sal_Int32>(rScenePosition3D.PositionY) ); + + //transformation from scene to screen (only necessary for 3D): + if(nDimensionCount==3) + { + //create 3D anchor shape + tPropertyNameMap aDummyPropertyNameMap; + rtl::Reference<Svx3DExtrudeObject> xShape3DAnchor = ShapeFactory::createCube( xSceneTarget + , rScenePosition3D,drawing::Direction3D(1,1,1) + , 0, nullptr, aDummyPropertyNameMap); + //get 2D position from xShape3DAnchor + aScreenPoint = xShape3DAnchor->getPosition(); + xSceneTarget->remove(xShape3DAnchor); + } + return aScreenPoint; +} + +void PlottingPositionHelper::transformScaledLogicToScene( drawing::PolyPolygonShape3D& rPolygon ) const +{ + drawing::Position3D aScenePosition; + auto SequenceXRange = asNonConstRange(rPolygon.SequenceX); + auto SequenceYRange = asNonConstRange(rPolygon.SequenceY); + auto SequenceZRange = asNonConstRange(rPolygon.SequenceZ); + for( sal_Int32 nS = rPolygon.SequenceX.getLength(); nS--;) + { + auto xValuesRange = asNonConstRange(SequenceXRange[nS]); + auto yValuesRange = asNonConstRange(SequenceYRange[nS]); + auto zValuesRange = asNonConstRange(SequenceZRange[nS]); + for( sal_Int32 nP = SequenceXRange[nS].getLength(); nP--; ) + { + double& fX = xValuesRange[nP]; + double& fY = yValuesRange[nP]; + double& fZ = zValuesRange[nP]; + aScenePosition = transformScaledLogicToScene( fX,fY,fZ,true ); + fX = aScenePosition.PositionX; + fY = aScenePosition.PositionY; + fZ = aScenePosition.PositionZ; + } + } +} + +void PlottingPositionHelper::transformScaledLogicToScene( std::vector<std::vector<css::drawing::Position3D>>& rPolygon ) const +{ + drawing::Position3D aScenePosition; + for( sal_Int32 nS = static_cast<sal_Int32>(rPolygon.size()); nS--;) + { + auto valuesRange = rPolygon[nS].data(); + for( sal_Int32 nP = rPolygon[nS].size(); nP--; ) + { + double& fX = valuesRange[nP].PositionX; + double& fY = valuesRange[nP].PositionY; + double& fZ = valuesRange[nP].PositionZ; + aScenePosition = transformScaledLogicToScene( fX,fY,fZ,true ); + fX = aScenePosition.PositionX; + fY = aScenePosition.PositionY; + fZ = aScenePosition.PositionZ; + } + } +} + +void PlottingPositionHelper::clipScaledLogicValues( double* pX, double* pY, double* pZ ) const +{ + //get logic clip values: + double MinX = getLogicMinX(); + double MinY = getLogicMinY(); + double MinZ = getLogicMinZ(); + double MaxX = getLogicMaxX(); + double MaxY = getLogicMaxY(); + double MaxZ = getLogicMaxZ(); + + //apply scaling + doUnshiftedLogicScaling( &MinX, &MinY, &MinZ ); + doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ); + + if(pX) + { + if( *pX < MinX ) + *pX = MinX; + else if( *pX > MaxX ) + *pX = MaxX; + } + if(pY) + { + if( *pY < MinY ) + *pY = MinY; + else if( *pY > MaxY ) + *pY = MaxY; + } + if(pZ) + { + if( *pZ < MinZ ) + *pZ = MinZ; + else if( *pZ > MaxZ ) + *pZ = MaxZ; + } +} + +basegfx::B2DRectangle PlottingPositionHelper::getScaledLogicClipDoubleRect() const +{ + //get logic clip values: + double MinX = getLogicMinX(); + double MinY = getLogicMinY(); + double MinZ = getLogicMinZ(); + double MaxX = getLogicMaxX(); + double MaxY = getLogicMaxY(); + double MaxZ = getLogicMaxZ(); + + //apply scaling + doUnshiftedLogicScaling( &MinX, &MinY, &MinZ ); + doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ); + + basegfx::B2DRectangle aRet( MinX, MaxY, MaxX, MinY ); + return aRet; +} + +drawing::Direction3D PlottingPositionHelper::getScaledLogicWidth() const +{ + drawing::Direction3D aRet; + + double MinX = getLogicMinX(); + double MinY = getLogicMinY(); + double MinZ = getLogicMinZ(); + double MaxX = getLogicMaxX(); + double MaxY = getLogicMaxY(); + double MaxZ = getLogicMaxZ(); + + doLogicScaling( &MinX, &MinY, &MinZ ); + doLogicScaling( &MaxX, &MaxY, &MaxZ); + + aRet.DirectionX = MaxX - MinX; + aRet.DirectionY = MaxY - MinY; + aRet.DirectionZ = MaxZ - MinZ; + return aRet; +} + +PolarPlottingPositionHelper::PolarPlottingPositionHelper() + : m_fRadiusOffset(0.0) + , m_fAngleDegreeOffset(90.0) +{ + m_bMaySkipPointsInRegressionCalculation = false; +} + +PolarPlottingPositionHelper::PolarPlottingPositionHelper( const PolarPlottingPositionHelper& rSource ) + : PlottingPositionHelper(rSource) + , m_fRadiusOffset( rSource.m_fRadiusOffset ) + , m_fAngleDegreeOffset( rSource.m_fAngleDegreeOffset ) + , m_aUnitCartesianToScene( rSource.m_aUnitCartesianToScene ) +{ +} + +PolarPlottingPositionHelper::~PolarPlottingPositionHelper() +{ +} + +std::unique_ptr<PlottingPositionHelper> PolarPlottingPositionHelper::clone() const +{ + return std::make_unique<PolarPlottingPositionHelper>(*this); +} + +void PolarPlottingPositionHelper::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix) +{ + PlottingPositionHelper::setTransformationSceneToScreen( rMatrix); + m_aUnitCartesianToScene =impl_calculateMatrixUnitCartesianToScene( m_aMatrixScreenToScene ); +} +void PolarPlottingPositionHelper::setScales( std::vector< ExplicitScaleData >&& rScales, bool bSwapXAndYAxis ) +{ + PlottingPositionHelper::setScales( std::move(rScales), bSwapXAndYAxis ); + m_aUnitCartesianToScene =impl_calculateMatrixUnitCartesianToScene( m_aMatrixScreenToScene ); +} + +::basegfx::B3DHomMatrix PolarPlottingPositionHelper::impl_calculateMatrixUnitCartesianToScene( const ::basegfx::B3DHomMatrix& rMatrixScreenToScene ) const +{ + ::basegfx::B3DHomMatrix aRet; + + if( m_aScales.empty() ) + return aRet; + + double fTranslate =1.0; + double fScale =FIXED_SIZE_FOR_3D_CHART_VOLUME/2.0; + + double fTranslateLogicZ; + double fScaleLogicZ; + { + double fScaleDirectionZ = m_aScales[2].Orientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0; + double MinZ = getLogicMinZ(); + double MaxZ = getLogicMaxZ(); + doLogicScaling( nullptr, nullptr, &MinZ ); + doLogicScaling( nullptr, nullptr, &MaxZ ); + double fWidthZ = MaxZ - MinZ; + + if( m_aScales[2].Orientation==AxisOrientation_MATHEMATICAL ) + fTranslateLogicZ=MinZ; + else + fTranslateLogicZ=MaxZ; + fScaleLogicZ = fScaleDirectionZ*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthZ; + } + + double fTranslateX = fTranslate; + double fTranslateY = fTranslate; + double fTranslateZ = fTranslateLogicZ; + + double fScaleX = fScale; + double fScaleY = fScale; + double fScaleZ = fScaleLogicZ; + + aRet.translate(fTranslateX, fTranslateY, fTranslateZ);//x first + aRet.scale(fScaleX, fScaleY, fScaleZ);//x first + + aRet = rMatrixScreenToScene * aRet; + return aRet; +} + +::chart::XTransformation2* PolarPlottingPositionHelper::getTransformationScaledLogicToScene() const +{ + if( !m_xTransformationLogicToScene ) + m_xTransformationLogicToScene.reset(new VPolarTransformation(*this)); + return m_xTransformationLogicToScene.get(); +} + +double PolarPlottingPositionHelper::getWidthAngleDegree( double& fStartLogicValueOnAngleAxis, double& fEndLogicValueOnAngleAxis ) const +{ + const ExplicitScaleData& rAngleScale = m_bSwapXAndY ? m_aScales[1] : m_aScales[0]; + if( rAngleScale.Orientation != AxisOrientation_MATHEMATICAL ) + { + double fHelp = fEndLogicValueOnAngleAxis; + fEndLogicValueOnAngleAxis = fStartLogicValueOnAngleAxis; + fStartLogicValueOnAngleAxis = fHelp; + } + + double fStartAngleDegree = transformToAngleDegree( fStartLogicValueOnAngleAxis ); + double fEndAngleDegree = transformToAngleDegree( fEndLogicValueOnAngleAxis ); + double fWidthAngleDegree = fEndAngleDegree - fStartAngleDegree; + + if( ::rtl::math::approxEqual( fStartAngleDegree, fEndAngleDegree ) + && !::rtl::math::approxEqual( fStartLogicValueOnAngleAxis, fEndLogicValueOnAngleAxis ) ) + fWidthAngleDegree = 360.0; + + // tdf#123504: both 0 and 360 are valid and different values here! + while (fWidthAngleDegree < 0.0) + fWidthAngleDegree += 360.0; + while (fWidthAngleDegree > 360.0) + fWidthAngleDegree -= 360.0; + + return fWidthAngleDegree; +} + +//This method does a lot of computation for understanding which scale to +//utilize and if reverse orientation should be used. Indeed, for a pie or donut, +//the final result is as simple as multiplying by 360 and adding +//`m_fAngleDegreeOffset`. +double PolarPlottingPositionHelper::transformToAngleDegree( double fLogicValueOnAngleAxis, bool bDoScaling ) const +{ + double fRet=0.0; + + double fAxisAngleScaleDirection = 1.0; + { + const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[1] : m_aScales[0]; + if(rScale.Orientation != AxisOrientation_MATHEMATICAL) + fAxisAngleScaleDirection *= -1.0; + } + + double MinAngleValue = 0.0; + double MaxAngleValue = 0.0; + { + double MinX = getLogicMinX(); + double MinY = getLogicMinY(); + double MaxX = getLogicMaxX(); + double MaxY = getLogicMaxY(); + double MinZ = getLogicMinZ(); + double MaxZ = getLogicMaxZ(); + + doLogicScaling( &MinX, &MinY, &MinZ ); + doLogicScaling( &MaxX, &MaxY, &MaxZ); + + MinAngleValue = m_bSwapXAndY ? MinY : MinX; + MaxAngleValue = m_bSwapXAndY ? MaxY : MaxX; + } + + double fScaledLogicAngleValue = 0.0; + if(bDoScaling) + { + double fX = m_bSwapXAndY ? getLogicMaxX() : fLogicValueOnAngleAxis; + double fY = m_bSwapXAndY ? fLogicValueOnAngleAxis : getLogicMaxY(); + double fZ = getLogicMaxZ(); + clipLogicValues( &fX, &fY, &fZ ); + doLogicScaling( &fX, &fY, &fZ ); + fScaledLogicAngleValue = m_bSwapXAndY ? fY : fX; + } + else + fScaledLogicAngleValue = fLogicValueOnAngleAxis; + + fRet = m_fAngleDegreeOffset + + fAxisAngleScaleDirection*(fScaledLogicAngleValue-MinAngleValue)*360.0 + /fabs(MaxAngleValue-MinAngleValue); + // tdf#123504: both 0 and 360 are valid and different values here! + while (fRet > 360.0) + fRet -= 360.0; + while (fRet < 0) + fRet += 360.0; + return fRet; +} + +/** + * Given a value in the radius axis scale range, it returns, in the simplest + * case (that is when `m_fRadiusOffset` is zero), the normalized value; when + * `m_fRadiusOffset` is not zero (e.g. as in the case of a donut), the interval + * used for normalization is extended by `m_fRadiusOffset`: if the axis + * orientation is not reversed the new interval becomes + * [scale.Minimum - m_fRadiusOffset, scale.Maximum] else it becomes + * [scale.Minimum, scale.Maximum + m_fRadiusOffset]. + * Pay attention here! For the latter case, since the axis orientation is + * reversed, the normalization is reversed too. Indeed, we have + * `transformToRadius(scale.Maximum + m_fRadiusOffset) = 0` and + * `transformToRadius(scale.Minimum) = 1`. + * + * For a pie chart the radius axis scale range is initialized by the + * `getMinimum` and `getMaximum` methods of the `PieChart` object (see notes + * for `VCoordinateSystem::prepareAutomaticAxisScaling`). + * So we have scale.Minimum = 0.5 (always constant!) and + * scale.Maximum = 0.5 + number_of_rings + max_offset + * (see notes for `PieChart::getMaxOffset`). + * Hence we get the following general formulas for computing normalized inner + * and outer radius: + * + * 1- transformToRadius(inner_radius) = + * (number_of_rings - (ring_index + 1) + m_fRadiusOffset) + * / (number_of_rings + max_offset + m_fRadiusOffset) + * + * 2- transformToRadius(outer_radius) = + * (1 + number_of_rings - (ring_index + 1) + m_fRadiusOffset) + * / (number_of_rings + max_offset + m_fRadiusOffset). + * + * Here you have to take into account that values for inner and outer radius + * are swapped since the radius axis is reversed (See notes for + * `PiePositionHelper::getInnerAndOuterRadius`). So indeed inner_radius is + * the outer and outer_radius is the inner. Anyway still because of the reverse + * orientation, the normalization performed by `transformToRadius` is reversed + * too, as we have seen above. Hence `transformToRadius(inner_radius)` is + * really the normalized inner radius and `transformToRadius(outer_radius)` is + * really the normalized outer radius. + * + * Some basic examples where we apply the above formulas: + * 1- For a non-exploded pie chart we have: + * `transformToRadius(inner_radius) = 0`, + * `transformToRadius(outer_radius) = 1`. + * 2- For a non-exploded donut with a single ring we have: + * `transformToRadius(inner_radius) = + * m_fRadiusOffset/(1 + m_fRadiusOffset)`, + * `transformToRadius(outer_radius) = + * (1 + m_fRadiusOffset)/(1 + m_fRadiusOffset) = 1`. + * 3- For an exploded pie chart we have: + * `transformToRadius(inner_radius) = 0/(1 + max_offset) = 0`, + * `transformToRadius(outer_radius) = 1/(1 + max_offset)`. + * + * The third example needs some remark. Both the logical inner and outer + * radius passed to `transformToRadius` are offset by `max_offset`. + * However the returned normalized values do not contain any (normalized) + * offset term at all, otherwise the returned values would be + * `max_offset/(1 + max_offset)` and `1`. Hence, for exploded pie/donut, + * `transformToRadius` returns the normalized value of radii without any + * offset term. These values are smaller than in the non-exploded case by an + * amount equals to the value of the normalized maximum offset + * (`max_offset/(1 + max_offset)` in the example above). That is due to the + * fact that the normalization keeps into account the space needed for the + * offset. This is the correct behavior, in fact the offset for the current + * slice could be different from the maximum offset. + * These remarks should clarify why the `PieChart::createDataPoint` and + * `PieChart::createTextLabelShape` methods add the normalized offset (for the + * current slice) to the normalized radii in order to achieve the correct + * placement of slice and text shapes. + */ +double PolarPlottingPositionHelper::transformToRadius( double fLogicValueOnRadiusAxis, bool bDoScaling ) const +{ + double fNormalRadius = 0.0; + { + double fScaledLogicRadiusValue = 0.0; + double fX = m_bSwapXAndY ? fLogicValueOnRadiusAxis: getLogicMaxX(); + double fY = m_bSwapXAndY ? getLogicMaxY() : fLogicValueOnRadiusAxis; + if(bDoScaling) + doLogicScaling( &fX, &fY, nullptr ); + + fScaledLogicRadiusValue = m_bSwapXAndY ? fX : fY; + + bool bMinIsInnerRadius = true; + const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[0] : m_aScales[1]; + if(rScale.Orientation != AxisOrientation_MATHEMATICAL) + bMinIsInnerRadius = false; + + double fInnerScaledLogicRadius=0.0; + double fOuterScaledLogicRadius=0.0; + { + double MinX = getLogicMinX(); + double MinY = getLogicMinY(); + doLogicScaling( &MinX, &MinY, nullptr ); + double MaxX = getLogicMaxX(); + double MaxY = getLogicMaxY(); + doLogicScaling( &MaxX, &MaxY, nullptr ); + + double fMin = m_bSwapXAndY ? MinX : MinY; + double fMax = m_bSwapXAndY ? MaxX : MaxY; + + fInnerScaledLogicRadius = bMinIsInnerRadius ? fMin : fMax; + fOuterScaledLogicRadius = bMinIsInnerRadius ? fMax : fMin; + } + + if( bMinIsInnerRadius ) + fInnerScaledLogicRadius -= fabs(m_fRadiusOffset); + else + fInnerScaledLogicRadius += fabs(m_fRadiusOffset); + fNormalRadius = (fScaledLogicRadiusValue-fInnerScaledLogicRadius)/(fOuterScaledLogicRadius-fInnerScaledLogicRadius); + } + return fNormalRadius; +} + +drawing::Position3D PolarPlottingPositionHelper::transformLogicToScene( double fX, double fY, double fZ, bool bClip ) const +{ + if(bClip) + clipLogicValues( &fX,&fY,&fZ ); + double fLogicValueOnAngleAxis = m_bSwapXAndY ? fY : fX; + double fLogicValueOnRadiusAxis = m_bSwapXAndY ? fX : fY; + return transformAngleRadiusToScene( fLogicValueOnAngleAxis, fLogicValueOnRadiusAxis, fZ ); +} + +drawing::Position3D PolarPlottingPositionHelper::transformScaledLogicToScene( double fX, double fY, double fZ, bool bClip ) const +{ + if(bClip) + clipScaledLogicValues( &fX,&fY,&fZ ); + double fLogicValueOnAngleAxis = m_bSwapXAndY ? fY : fX; + double fLogicValueOnRadiusAxis = m_bSwapXAndY ? fX : fY; + return transformAngleRadiusToScene( fLogicValueOnAngleAxis, fLogicValueOnRadiusAxis, fZ, false ); +} +drawing::Position3D PolarPlottingPositionHelper::transformUnitCircleToScene( double fUnitAngleDegree, double fUnitRadius + , double fLogicZ ) const +{ + double fAnglePi = basegfx::deg2rad(fUnitAngleDegree); + + double fX=fUnitRadius*std::cos(fAnglePi); + double fY=fUnitRadius*std::sin(fAnglePi); + double fZ=fLogicZ; + + //!! applying matrix to vector does ignore translation, so it is important to use a B3DPoint here instead of B3DVector + ::basegfx::B3DPoint aPoint(fX,fY,fZ); + ::basegfx::B3DPoint aRet = m_aUnitCartesianToScene * aPoint; + return B3DPointToPosition3D(aRet); +} + +drawing::Position3D PolarPlottingPositionHelper::transformAngleRadiusToScene( double fLogicValueOnAngleAxis, double fLogicValueOnRadiusAxis, double fLogicZ, bool bDoScaling ) const +{ + double fUnitAngleDegree = transformToAngleDegree(fLogicValueOnAngleAxis,bDoScaling); + double fUnitRadius = transformToRadius(fLogicValueOnRadiusAxis,bDoScaling); + + return transformUnitCircleToScene( fUnitAngleDegree, fUnitRadius, fLogicZ ); +} + +double PolarPlottingPositionHelper::getOuterLogicRadius() const +{ + const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[0] : m_aScales[1]; + if( rScale.Orientation==AxisOrientation_MATHEMATICAL ) + return rScale.Maximum; + else + return rScale.Minimum; +} + +bool PlottingPositionHelper::isPercentY() const +{ + return m_aScales[1].AxisType==AxisType::PERCENT; +} + +double PlottingPositionHelper::getBaseValueY() const +{ + return m_aScales[1].Origin; +} + +void PlottingPositionHelper::setTimeResolution( tools::Long nTimeResolution, const Date& rNullDate ) +{ + m_nTimeResolution = nTimeResolution; + m_aNullDate = rNullDate; + + //adapt category width + double fCategoryWidth = 1.0; + if( !m_aScales.empty() ) + { + if( m_aScales[0].AxisType == css::chart2::AxisType::DATE ) + { + m_bDateAxis = true; + if( nTimeResolution == css::chart::TimeUnit::YEAR ) + { + const double fMonthCount = 12.0;//todo: this depends on the DateScaling and must be adjusted in case we use more generic calendars in future + fCategoryWidth = fMonthCount; + } + } + } + setScaledCategoryWidth(fCategoryWidth); +} + +void PlottingPositionHelper::setScaledCategoryWidth( double fScaledCategoryWidth ) +{ + m_fScaledCategoryWidth = fScaledCategoryWidth; +} +void PlottingPositionHelper::AllowShiftXAxisPos( bool bAllowShift ) +{ + m_bAllowShiftXAxisPos = bAllowShift; +} +void PlottingPositionHelper::AllowShiftZAxisPos( bool bAllowShift ) +{ + m_bAllowShiftZAxisPos = bAllowShift; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |