LCOV - code coverage report
Current view: top level - libreoffice/chart2/source/view/charttypes - PieChart.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 0 433 0.0 %
Date: 2012-12-27 Functions: 0 32 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       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/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      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 .
      18             :  */
      19             : 
      20             : #include "PieChart.hxx"
      21             : #include "PlottingPositionHelper.hxx"
      22             : #include "ShapeFactory.hxx"
      23             : #include "PolarLabelPositionHelper.hxx"
      24             : #include "macros.hxx"
      25             : #include "CommonConverters.hxx"
      26             : #include "ViewDefines.hxx"
      27             : #include "ObjectIdentifier.hxx"
      28             : 
      29             : #include <com/sun/star/chart/DataLabelPlacement.hpp>
      30             : #include <com/sun/star/chart2/XColorScheme.hpp>
      31             : 
      32             : #include <com/sun/star/container/XChild.hpp>
      33             : #include <rtl/math.hxx>
      34             : 
      35             : #include <boost/scoped_ptr.hpp>
      36             : 
      37             : //.............................................................................
      38             : namespace chart
      39             : {
      40             : //.............................................................................
      41             : using namespace ::com::sun::star;
      42             : using namespace ::com::sun::star::chart2;
      43             : 
      44             : class PiePositionHelper : public PolarPlottingPositionHelper
      45             : {
      46             : public:
      47             :     PiePositionHelper( NormalAxis eNormalAxis, double fAngleDegreeOffset );
      48             :     virtual ~PiePositionHelper();
      49             : 
      50             :     bool    getInnerAndOuterRadius( double fCategoryX, double& fLogicInnerRadius, double& fLogicOuterRadius, bool bUseRings, double fMaxOffset ) const;
      51             : 
      52             : public:
      53             :     //Distance between different category rings, seen relative to width of a ring:
      54             :     double  m_fRingDistance; //>=0 m_fRingDistance=1 --> distance == width
      55             : };
      56             : 
      57           0 : PiePositionHelper::PiePositionHelper( NormalAxis eNormalAxis, double fAngleDegreeOffset )
      58             :         : PolarPlottingPositionHelper(eNormalAxis)
      59           0 :         , m_fRingDistance(0.0)
      60             : {
      61           0 :     m_fRadiusOffset = 0.0;
      62           0 :     m_fAngleDegreeOffset = fAngleDegreeOffset;
      63           0 : }
      64             : 
      65           0 : PiePositionHelper::~PiePositionHelper()
      66             : {
      67           0 : }
      68             : 
      69           0 : bool PiePositionHelper::getInnerAndOuterRadius( double fCategoryX
      70             :                                                , double& fLogicInnerRadius, double& fLogicOuterRadius
      71             :                                                , bool bUseRings, double fMaxOffset ) const
      72             : {
      73           0 :     if( !bUseRings )
      74           0 :         fCategoryX = 1.0;
      75             : 
      76           0 :     bool bIsVisible = true;
      77           0 :     double fLogicInner = fCategoryX -0.5+m_fRingDistance/2.0;
      78           0 :     double fLogicOuter = fCategoryX +0.5-m_fRingDistance/2.0;
      79             : 
      80           0 :     if( !isMathematicalOrientationRadius() )
      81             :     {
      82             :         //in this case the given getMaximumX() was not corrcect instead the minimum should have been smaller by fMaxOffset
      83             :         //but during getMaximumX and getMimumX we do not know the axis orientation
      84           0 :         fLogicInner += fMaxOffset;
      85           0 :         fLogicOuter += fMaxOffset;
      86             :     }
      87             : 
      88           0 :     if( fLogicInner >= getLogicMaxX() )
      89           0 :         return false;
      90           0 :     if( fLogicOuter <= getLogicMinX() )
      91           0 :         return false;
      92             : 
      93           0 :     if( fLogicInner < getLogicMinX() )
      94           0 :         fLogicInner = getLogicMinX();
      95           0 :     if( fLogicOuter > getLogicMaxX() )
      96           0 :         fLogicOuter = getLogicMaxX();
      97             : 
      98           0 :     fLogicInnerRadius = fLogicInner;
      99           0 :     fLogicOuterRadius = fLogicOuter;
     100           0 :     if( !isMathematicalOrientationRadius() )
     101           0 :         std::swap(fLogicInnerRadius,fLogicOuterRadius);
     102           0 :     return bIsVisible;
     103             : }
     104             : 
     105             : //-----------------------------------------------------------------------------
     106             : //-----------------------------------------------------------------------------
     107             : //-----------------------------------------------------------------------------
     108             : 
     109           0 : PieChart::PieChart( const uno::Reference<XChartType>& xChartTypeModel
     110             :                    , sal_Int32 nDimensionCount
     111             :                    , bool bExcludingPositioning )
     112             :         : VSeriesPlotter( xChartTypeModel, nDimensionCount )
     113           0 :         , m_pPosHelper( new PiePositionHelper( NormalAxis_Z, (m_nDimension==3)?0.0:90.0 ) )
     114             :         , m_bUseRings(false)
     115           0 :         , m_bSizeExcludesLabelsAndExplodedSegments(bExcludingPositioning)
     116             : {
     117           0 :     ::rtl::math::setNan(&m_fMaxOffset);
     118             : 
     119           0 :     PlotterBase::m_pPosHelper = m_pPosHelper;
     120           0 :     VSeriesPlotter::m_pMainPosHelper = m_pPosHelper;
     121           0 :     m_pPosHelper->m_fRadiusOffset = 0.0;
     122           0 :     m_pPosHelper->m_fRingDistance = 0.0;
     123             : 
     124           0 :     uno::Reference< beans::XPropertySet > xChartTypeProps( xChartTypeModel, uno::UNO_QUERY );
     125           0 :     if( xChartTypeProps.is() ) try
     126             :     {
     127           0 :         xChartTypeProps->getPropertyValue( C2U( "UseRings" )) >>= m_bUseRings;
     128           0 :         if( m_bUseRings )
     129             :         {
     130           0 :             m_pPosHelper->m_fRadiusOffset = 1.0;
     131           0 :             if( nDimensionCount==3 )
     132           0 :                 m_pPosHelper->m_fRingDistance = 0.1;
     133             :         }
     134             :     }
     135           0 :     catch( const uno::Exception& e )
     136             :     {
     137             :         ASSERT_EXCEPTION( e );
     138           0 :     }
     139           0 : }
     140             : 
     141           0 : PieChart::~PieChart()
     142             : {
     143           0 :     delete m_pPosHelper;
     144           0 : }
     145             : 
     146             : //-----------------------------------------------------------------
     147             : 
     148           0 : void PieChart::setScales( const std::vector< ExplicitScaleData >& rScales, bool /* bSwapXAndYAxis */ )
     149             : {
     150             :     OSL_ENSURE(m_nDimension<=static_cast<sal_Int32>(rScales.size()),"Dimension of Plotter does not fit two dimension of given scale sequence");
     151           0 :     m_pPosHelper->setScales( rScales, true );
     152           0 : }
     153             : 
     154             : //-----------------------------------------------------------------
     155             : 
     156           0 : drawing::Direction3D PieChart::getPreferredDiagramAspectRatio() const
     157             : {
     158           0 :     if( m_nDimension == 3 )
     159           0 :         return drawing::Direction3D(1,1,0.25);
     160           0 :     return drawing::Direction3D(1,1,1);
     161             : }
     162             : 
     163           0 : bool PieChart::keepAspectRatio() const
     164             : {
     165           0 :     if( m_nDimension == 3 )
     166           0 :         return false;
     167           0 :     return true;
     168             : }
     169             : 
     170           0 : bool PieChart::shouldSnapRectToUsedArea()
     171             : {
     172           0 :     return true;
     173             : }
     174             : 
     175           0 : uno::Reference< drawing::XShape > PieChart::createDataPoint(
     176             :           const uno::Reference< drawing::XShapes >& xTarget
     177             :         , const uno::Reference< beans::XPropertySet >& xObjectProperties
     178             :         , double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree
     179             :         , double fUnitCircleInnerRadius, double fUnitCircleOuterRadius
     180             :         , double fLogicZ, double fDepth, double fExplodePercentage
     181             :         , tPropertyNameValueMap* pOverwritePropertiesMap )
     182             : {
     183             :     //---------------------------
     184             :     //transform position:
     185           0 :     drawing::Direction3D aOffset;
     186           0 :     if( !::rtl::math::approxEqual( fExplodePercentage, 0.0 ) )
     187             :     {
     188           0 :         double fAngle  = fUnitCircleStartAngleDegree + fUnitCircleWidthAngleDegree/2.0;
     189           0 :         double fRadius = (fUnitCircleOuterRadius-fUnitCircleInnerRadius)*fExplodePercentage;
     190           0 :         drawing::Position3D aOrigin = m_pPosHelper->transformUnitCircleToScene( 0, 0, fLogicZ );
     191           0 :         drawing::Position3D aNewOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, fRadius, fLogicZ );
     192           0 :         aOffset = aNewOrigin - aOrigin;
     193             :     }
     194             : 
     195             :     //---------------------------
     196             :     //create point
     197           0 :     uno::Reference< drawing::XShape > xShape(0);
     198           0 :     if(m_nDimension==3)
     199             :     {
     200             :         xShape = m_pShapeFactory->createPieSegment( xTarget
     201             :             , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree
     202             :             , fUnitCircleInnerRadius, fUnitCircleOuterRadius
     203           0 :             , aOffset, B3DHomMatrixToHomogenMatrix( m_pPosHelper->getUnitCartesianToScene() )
     204           0 :             , fDepth );
     205             :     }
     206             :     else
     207             :     {
     208             :         xShape = m_pShapeFactory->createPieSegment2D( xTarget
     209             :             , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree
     210             :             , fUnitCircleInnerRadius, fUnitCircleOuterRadius
     211           0 :             , aOffset, B3DHomMatrixToHomogenMatrix( m_pPosHelper->getUnitCartesianToScene() ) );
     212             :     }
     213           0 :     this->setMappedProperties( xShape, xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), pOverwritePropertiesMap );
     214           0 :     return xShape;
     215             : }
     216             : 
     217           0 : void PieChart::addSeries( VDataSeries* pSeries, sal_Int32 /* zSlot */, sal_Int32 /* xSlot */, sal_Int32 /* ySlot */ )
     218             : {
     219           0 :     VSeriesPlotter::addSeries( pSeries, 0, -1, 0 );
     220           0 : }
     221             : 
     222           0 : double PieChart::getMinimumX()
     223             : {
     224           0 :     return 0.5;
     225             : }
     226           0 : double PieChart::getMaxOffset()
     227             : {
     228           0 :     if (!::rtl::math::isNan(m_fMaxOffset))
     229             :         // Value already cached.  Use it.
     230           0 :         return m_fMaxOffset;
     231             : 
     232           0 :     m_fMaxOffset = 0.0;
     233           0 :     if( m_aZSlots.size()<=0 )
     234           0 :         return m_fMaxOffset;
     235           0 :     if( m_aZSlots[0].size()<=0 )
     236           0 :         return m_fMaxOffset;
     237             : 
     238           0 :     const ::std::vector< VDataSeries* >& rSeriesList( m_aZSlots[0][0].m_aSeriesVector );
     239           0 :     if( rSeriesList.size()<=0 )
     240           0 :         return m_fMaxOffset;
     241             : 
     242           0 :     VDataSeries* pSeries = rSeriesList[0];
     243           0 :     uno::Reference< beans::XPropertySet > xSeriesProp( pSeries->getPropertiesOfSeries() );
     244           0 :     if( !xSeriesProp.is() )
     245           0 :         return m_fMaxOffset;
     246             : 
     247           0 :     double fExplodePercentage=0.0;
     248           0 :     xSeriesProp->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage;
     249           0 :     if(fExplodePercentage>m_fMaxOffset)
     250           0 :         m_fMaxOffset=fExplodePercentage;
     251             : 
     252           0 :     if(!m_bSizeExcludesLabelsAndExplodedSegments)
     253             :     {
     254           0 :         uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
     255           0 :         if( xSeriesProp->getPropertyValue( C2U( "AttributedDataPoints" ) ) >>= aAttributedDataPointIndexList )
     256             :         {
     257           0 :             for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
     258             :             {
     259           0 :                 uno::Reference< beans::XPropertySet > xPointProp( pSeries->getPropertiesOfPoint(aAttributedDataPointIndexList[nN]) );
     260           0 :                 if(xPointProp.is())
     261             :                 {
     262           0 :                     fExplodePercentage=0.0;
     263           0 :                     xPointProp->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage;
     264           0 :                     if(fExplodePercentage>m_fMaxOffset)
     265           0 :                         m_fMaxOffset=fExplodePercentage;
     266             :                 }
     267           0 :             }
     268           0 :         }
     269             :     }
     270           0 :     return m_fMaxOffset;
     271             : }
     272           0 : double PieChart::getMaximumX()
     273             : {
     274           0 :     double fMaxOffset = getMaxOffset();
     275           0 :     if( m_aZSlots.size()>0 && m_bUseRings)
     276           0 :         return m_aZSlots[0].size()+0.5+fMaxOffset;
     277           0 :     return 1.5+fMaxOffset;
     278             : }
     279           0 : double PieChart::getMinimumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32 /* nAxisIndex */ )
     280             : {
     281           0 :     return 0.0;
     282             : }
     283             : 
     284           0 : double PieChart::getMaximumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32 /* nAxisIndex */ )
     285             : {
     286           0 :     return 1.0;
     287             : }
     288             : 
     289           0 : bool PieChart::isExpandBorderToIncrementRhythm( sal_Int32 /* nDimensionIndex */ )
     290             : {
     291           0 :     return false;
     292             : }
     293             : 
     294           0 : bool PieChart::isExpandIfValuesCloseToBorder( sal_Int32 /* nDimensionIndex */ )
     295             : {
     296           0 :     return false;
     297             : }
     298             : 
     299           0 : bool PieChart::isExpandWideValuesToZero( sal_Int32 /* nDimensionIndex */ )
     300             : {
     301           0 :     return false;
     302             : }
     303             : 
     304           0 : bool PieChart::isExpandNarrowValuesTowardZero( sal_Int32 /* nDimensionIndex */ )
     305             : {
     306           0 :     return false;
     307             : }
     308             : 
     309           0 : bool PieChart::isSeperateStackingForDifferentSigns( sal_Int32 /* nDimensionIndex */ )
     310             : {
     311           0 :     return false;
     312             : }
     313             : 
     314           0 : void PieChart::createShapes()
     315             : {
     316           0 :     if( m_aZSlots.begin() == m_aZSlots.end() ) //no series
     317             :         return;
     318             : 
     319             :     OSL_ENSURE(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"PieChart is not proper initialized");
     320           0 :     if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is()))
     321             :         return;
     322             : 
     323             :     //the text labels should be always on top of the other series shapes
     324             :     //therefore create an own group for the texts to move them to front
     325             :     //(because the text group is created after the series group the texts are displayed on top)
     326             :     uno::Reference< drawing::XShapes > xSeriesTarget(
     327           0 :         createGroupShape( m_xLogicTarget,rtl::OUString() ));
     328             :     uno::Reference< drawing::XShapes > xTextTarget(
     329           0 :         m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() ));
     330             :     //---------------------------------------------
     331             :     //check necessary here that different Y axis can not be stacked in the same group? ... hm?
     332             : 
     333             : //=============================================================================
     334           0 :     ::std::vector< VDataSeriesGroup >::iterator             aXSlotIter = m_aZSlots[0].begin();
     335           0 :     const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = m_aZSlots[0].end();
     336             : 
     337           0 :     ::std::vector< VDataSeriesGroup >::size_type nExplodeableSlot = 0;
     338           0 :     if( m_pPosHelper->isMathematicalOrientationRadius() && m_bUseRings )
     339           0 :         nExplodeableSlot = m_aZSlots[0].size()-1;
     340             : 
     341           0 :     m_aLabelInfoList.clear();
     342           0 :     ::rtl::math::setNan(&m_fMaxOffset);
     343             : 
     344             : //=============================================================================
     345           0 :     for( double fSlotX=0; aXSlotIter != aXSlotEnd && (m_bUseRings||fSlotX<0.5 ); ++aXSlotIter, fSlotX+=1.0 )
     346             :     {
     347           0 :         ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
     348           0 :         if( pSeriesList->size()<=0 )//there should be only one series in each x slot
     349           0 :             continue;
     350           0 :         VDataSeries* pSeries = (*pSeriesList)[0];
     351           0 :         if(!pSeries)
     352           0 :             continue;
     353             : 
     354           0 :         m_pPosHelper->m_fAngleDegreeOffset = pSeries->getStartingAngle();
     355             : 
     356           0 :         double fLogicYSum = 0.0;
     357             :         //iterate through all points to get the sum
     358           0 :         sal_Int32 nPointIndex=0;
     359           0 :         sal_Int32 nPointCount=pSeries->getTotalPointCount();
     360           0 :         for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ )
     361             :         {
     362           0 :             double fY = pSeries->getYValue( nPointIndex );
     363             :             if(fY<0.0)
     364             :             {
     365             :                 //@todo warn somehow that negative values are treated as positive
     366             :             }
     367           0 :             if( ::rtl::math::isNan(fY) )
     368           0 :                 continue;
     369           0 :             fLogicYSum += fabs(fY);
     370             :         }
     371           0 :         if(fLogicYSum==0.0)
     372           0 :             continue;
     373           0 :         double fLogicYForNextPoint = 0.0;
     374             :         //iterate through all points to create shapes
     375           0 :         for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ )
     376             :         {
     377             :             double fLogicInnerRadius, fLogicOuterRadius;
     378           0 :             double fOffset = getMaxOffset();
     379           0 :             bool bIsVisible = m_pPosHelper->getInnerAndOuterRadius( fSlotX+1.0, fLogicInnerRadius, fLogicOuterRadius, m_bUseRings, fOffset );
     380           0 :             if( !bIsVisible )
     381           0 :                 continue;
     382             : 
     383           0 :             double fDepth  = this->getTransformedDepth();
     384             : 
     385           0 :             uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries, xSeriesTarget);
     386             :             //collect data point information (logic coordinates, style ):
     387           0 :             double fLogicYValue = fabs(pSeries->getYValue( nPointIndex ));
     388           0 :             if( ::rtl::math::isNan(fLogicYValue) )
     389           0 :                 continue;
     390           0 :             if(fLogicYValue==0.0)//@todo: continue also if the resolution to small
     391           0 :                 continue;
     392           0 :             double fLogicYPos = fLogicYForNextPoint;
     393           0 :             fLogicYForNextPoint += fLogicYValue;
     394             : 
     395           0 :             uno::Reference< beans::XPropertySet > xPointProperties = pSeries->getPropertiesOfPoint( nPointIndex );
     396             : 
     397             :             //iterate through all subsystems to create partial points
     398             :             {
     399             :                 //logic values on angle axis:
     400           0 :                 double fLogicStartAngleValue = fLogicYPos/fLogicYSum;
     401           0 :                 double fLogicEndAngleValue = (fLogicYPos+fLogicYValue)/fLogicYSum;
     402             : 
     403           0 :                 double fExplodePercentage=0.0;
     404           0 :                 bool bDoExplode = ( nExplodeableSlot == static_cast< ::std::vector< VDataSeriesGroup >::size_type >(fSlotX) );
     405           0 :                 if(bDoExplode) try
     406             :                 {
     407           0 :                     xPointProperties->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage;
     408             :                 }
     409           0 :                 catch( const uno::Exception& e )
     410             :                 {
     411             :                     ASSERT_EXCEPTION( e );
     412             :                 }
     413             : 
     414             :                 //---------------------------
     415             :                 //transforme to unit circle:
     416           0 :                 double fUnitCircleWidthAngleDegree = m_pPosHelper->getWidthAngleDegree( fLogicStartAngleValue, fLogicEndAngleValue );
     417           0 :                 double fUnitCircleStartAngleDegree = m_pPosHelper->transformToAngleDegree( fLogicStartAngleValue );
     418           0 :                 double fUnitCircleInnerRadius = m_pPosHelper->transformToRadius( fLogicInnerRadius );
     419           0 :                 double fUnitCircleOuterRadius = m_pPosHelper->transformToRadius( fLogicOuterRadius );
     420             : 
     421             :                 //---------------------------
     422             :                 //point color:
     423           0 :                 boost::scoped_ptr< tPropertyNameValueMap > apOverwritePropertiesMap(NULL);
     424             :                 {
     425           0 :                     if(!pSeries->hasPointOwnColor(nPointIndex) && m_xColorScheme.is())
     426             :                     {
     427           0 :                         apOverwritePropertiesMap.reset( new tPropertyNameValueMap() );
     428           0 :                         (*apOverwritePropertiesMap)[C2U("FillColor")] = uno::makeAny(
     429           0 :                             m_xColorScheme->getColorByIndex( nPointIndex ));
     430             :                     }
     431             :                 }
     432             : 
     433             :                 //create data point
     434           0 :                 double fLogicZ = -1.0; // For 3D pie chart label position
     435             :                 uno::Reference<drawing::XShape> xPointShape(
     436             :                     createDataPoint( xSeriesGroupShape_Shapes, xPointProperties
     437             :                                     , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree
     438             :                                     , fUnitCircleInnerRadius, fUnitCircleOuterRadius
     439           0 :                                     , fLogicZ, fDepth, fExplodePercentage, apOverwritePropertiesMap.get() ) );
     440             : 
     441             :                 //create label
     442           0 :                 if( pSeries->getDataPointLabelIfLabel(nPointIndex) )
     443             :                 {
     444           0 :                     if( !::rtl::math::approxEqual( fExplodePercentage, 0.0 ) )
     445             :                     {
     446           0 :                         double fExplodeOffset = (fUnitCircleOuterRadius-fUnitCircleInnerRadius)*fExplodePercentage;
     447           0 :                         fUnitCircleInnerRadius += fExplodeOffset;
     448           0 :                         fUnitCircleOuterRadius += fExplodeOffset;
     449             :                     }
     450             : 
     451           0 :                     sal_Int32 nLabelPlacement = pSeries->getLabelPlacement( nPointIndex, m_xChartTypeModel, m_nDimension, m_pPosHelper->isSwapXAndY() );
     452             : 
     453             :                     // AVOID_OVERLAP is in fact "Best fit" in the UI.
     454           0 :                     bool bMovementAllowed = ( nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::AVOID_OVERLAP );
     455           0 :                     if( bMovementAllowed )
     456             :                         // Use center for "Best fit" for now. In the future we
     457             :                         // may want to implement a real best fit algorithm.
     458             :                         // But center is good enough, and close to what Excel
     459             :                         // does.
     460           0 :                         nLabelPlacement = ::com::sun::star::chart::DataLabelPlacement::CENTER;
     461             : 
     462           0 :                     LabelAlignment eAlignment(LABEL_ALIGN_CENTER);
     463           0 :                     sal_Int32 nScreenValueOffsetInRadiusDirection = 0 ;
     464           0 :                     if( nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::OUTSIDE )
     465           0 :                         nScreenValueOffsetInRadiusDirection = (3!=m_nDimension) ? 150 : 0;//todo maybe calculate this font height dependent
     466           0 :                     else if( nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::INSIDE )
     467           0 :                         nScreenValueOffsetInRadiusDirection = (3!=m_nDimension) ? -150 : 0;//todo maybe calculate this font height dependent
     468           0 :                     PolarLabelPositionHelper aPolarPosHelper(m_pPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory);
     469             :                     awt::Point aScreenPosition2D(
     470             :                         aPolarPosHelper.getLabelScreenPositionAndAlignmentForUnitCircleValues(eAlignment, nLabelPlacement
     471             :                         , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree
     472           0 :                         , fUnitCircleInnerRadius, fUnitCircleOuterRadius, fLogicZ+0.5, 0 ));
     473             : 
     474           0 :                     PieLabelInfo aPieLabelInfo;
     475           0 :                     aPieLabelInfo.aFirstPosition = basegfx::B2IVector( aScreenPosition2D.X, aScreenPosition2D.Y );
     476           0 :                     awt::Point aOrigin( aPolarPosHelper.transformSceneToScreenPosition( m_pPosHelper->transformUnitCircleToScene( 0.0, 0.0, fLogicZ+1.0 ) ) );
     477           0 :                     aPieLabelInfo.aOrigin = basegfx::B2IVector( aOrigin.X, aOrigin.Y );
     478             : 
     479             :                     //add a scaling independent Offset if requested
     480           0 :                     if( nScreenValueOffsetInRadiusDirection != 0)
     481             :                     {
     482           0 :                         basegfx::B2IVector aDirection( aScreenPosition2D.X- aOrigin.X, aScreenPosition2D.Y- aOrigin.Y );
     483           0 :                         aDirection.setLength(nScreenValueOffsetInRadiusDirection);
     484           0 :                         aScreenPosition2D.X += aDirection.getX();
     485           0 :                         aScreenPosition2D.Y += aDirection.getY();
     486             :                     }
     487             : 
     488             :                     aPieLabelInfo.xTextShape = this->createDataLabel( xTextTarget, *pSeries, nPointIndex
     489           0 :                                     , fLogicYValue, fLogicYSum, aScreenPosition2D, eAlignment );
     490             : 
     491           0 :                     uno::Reference< container::XChild > xChild( aPieLabelInfo.xTextShape, uno::UNO_QUERY );
     492           0 :                     if( xChild.is() )
     493           0 :                         aPieLabelInfo.xLabelGroupShape = uno::Reference<drawing::XShape>( xChild->getParent(), uno::UNO_QUERY );
     494           0 :                     aPieLabelInfo.fValue = fLogicYValue;
     495           0 :                     aPieLabelInfo.bMovementAllowed = bMovementAllowed;
     496           0 :                     aPieLabelInfo.bMoved= false;
     497           0 :                     aPieLabelInfo.xTextTarget = xTextTarget;
     498           0 :                     m_aLabelInfoList.push_back(aPieLabelInfo);
     499             :                 }
     500             : 
     501           0 :                 if(!bDoExplode)
     502             :                 {
     503             :                     ShapeFactory::setShapeName( xPointShape
     504           0 :                                 , ObjectIdentifier::createPointCID( pSeries->getPointCID_Stub(), nPointIndex ) );
     505             :                 }
     506             :                 else try
     507             :                 {
     508             :                     //enable dragging of outer segments
     509             : 
     510           0 :                     double fAngle  = fUnitCircleStartAngleDegree + fUnitCircleWidthAngleDegree/2.0;
     511           0 :                     double fMaxDeltaRadius = fUnitCircleOuterRadius-fUnitCircleInnerRadius;
     512           0 :                     drawing::Position3D aOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, fUnitCircleOuterRadius, fLogicZ );
     513           0 :                     drawing::Position3D aNewOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, fUnitCircleOuterRadius + fMaxDeltaRadius, fLogicZ );
     514             : 
     515           0 :                     sal_Int32 nOffsetPercent( static_cast<sal_Int32>(fExplodePercentage * 100.0) );
     516             : 
     517             :                     awt::Point aMinimumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
     518           0 :                         aOrigin, m_xLogicTarget, m_pShapeFactory, m_nDimension ) );
     519             :                     awt::Point aMaximumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
     520           0 :                         aNewOrigin, m_xLogicTarget, m_pShapeFactory, m_nDimension ) );
     521             : 
     522             :                     //enable draging of piesegments
     523             :                     rtl::OUString aPointCIDStub( ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT
     524             :                         , pSeries->getSeriesParticle()
     525           0 :                         , ObjectIdentifier::getPieSegmentDragMethodServiceName()
     526             :                         , ObjectIdentifier::createPieSegmentDragParameterString(
     527             :                             nOffsetPercent, aMinimumPosition, aMaximumPosition )
     528           0 :                         ) );
     529             : 
     530             :                     ShapeFactory::setShapeName( xPointShape
     531           0 :                                 , ObjectIdentifier::createPointCID( aPointCIDStub, nPointIndex ) );
     532             :                 }
     533           0 :                 catch( const uno::Exception& e )
     534             :                 {
     535             :                     ASSERT_EXCEPTION( e );
     536           0 :                 }
     537             :             }//next series in x slot (next y slot)
     538           0 :         }//next category
     539           0 :     }//next x slot
     540             : //=============================================================================
     541             : //=============================================================================
     542             : //=============================================================================
     543             :     /* @todo remove series shapes if empty
     544             :     //remove and delete point-group-shape if empty
     545             :     if(!xSeriesGroupShape_Shapes->getCount())
     546             :     {
     547             :         (*aSeriesIter)->m_xShape.set(NULL);
     548             :         m_xLogicTarget->remove(xSeriesGroupShape_Shape);
     549             :     }
     550             :     */
     551             : 
     552             :     //remove and delete series-group-shape if empty
     553             : 
     554             :     //... todo
     555             : }
     556             : 
     557             : namespace
     558             : {
     559             : 
     560           0 : ::basegfx::B2IRectangle lcl_getRect( const uno::Reference< drawing::XShape >& xShape )
     561             : {
     562           0 :     ::basegfx::B2IRectangle aRect;
     563           0 :     if( xShape.is() )
     564           0 :         aRect = BaseGFXHelper::makeRectangle(xShape->getPosition(),xShape->getSize() );
     565           0 :     return aRect;
     566             : }
     567             : 
     568           0 : bool lcl_isInsidePage( const awt::Point& rPos, const awt::Size& rSize, const awt::Size& rPageSize )
     569             : {
     570           0 :     if( rPos.X < 0  || rPos.Y < 0 )
     571           0 :         return false;
     572           0 :     if( (rPos.X + rSize.Width) > rPageSize.Width  )
     573           0 :         return false;
     574           0 :     if( (rPos.Y + rSize.Height) > rPageSize.Height )
     575           0 :         return false;
     576           0 :     return true;
     577             : }
     578             : 
     579             : }//end anonymous namespace
     580             : 
     581           0 : PieChart::PieLabelInfo::PieLabelInfo()
     582             :     : xTextShape(0), xLabelGroupShape(0), aFirstPosition(), aOrigin(), fValue(0.0)
     583           0 :     , bMovementAllowed(false), bMoved(false), xTextTarget(0), pPrevious(0),pNext(0)
     584             : {
     585           0 : }
     586             : 
     587           0 : bool PieChart::PieLabelInfo::moveAwayFrom( const PieChart::PieLabelInfo* pFix, const awt::Size& rPageSize, bool bMoveHalfWay, bool bMoveClockwise, bool bAlternativeMoveDirection )
     588             : {
     589             :     //return true if the move was successful
     590           0 :     if(!this->bMovementAllowed)
     591           0 :         return false;
     592             : 
     593           0 :     const sal_Int32 nLabelDistanceX = rPageSize.Width/50;
     594           0 :     const sal_Int32 nLabelDistanceY = rPageSize.Height/50;
     595             : 
     596           0 :     ::basegfx::B2IRectangle aOverlap( lcl_getRect( this->xLabelGroupShape ) );
     597           0 :     aOverlap.intersect( lcl_getRect( pFix->xLabelGroupShape ) );
     598           0 :     if( !aOverlap.isEmpty() )
     599             :     {
     600             :         (void)bAlternativeMoveDirection;//todo
     601             : 
     602           0 :         basegfx::B2IVector aRadiusDirection = this->aFirstPosition - this->aOrigin;
     603           0 :         aRadiusDirection.setLength(1.0);
     604           0 :         basegfx::B2IVector aTangentialDirection( -aRadiusDirection.getY(), aRadiusDirection.getX() );
     605           0 :         bool bShiftHorizontal = abs(aTangentialDirection.getX()) > abs(aTangentialDirection.getY());
     606             : 
     607           0 :         sal_Int32 nShift = bShiftHorizontal ? static_cast<sal_Int32>(aOverlap.getWidth()) : static_cast<sal_Int32>(aOverlap.getHeight());
     608           0 :         nShift += (bShiftHorizontal ? nLabelDistanceX : nLabelDistanceY);
     609           0 :         if( bMoveHalfWay )
     610           0 :             nShift/=2;
     611           0 :         if(!bMoveClockwise)
     612           0 :             nShift*=-1;
     613           0 :         awt::Point aOldPos( this->xLabelGroupShape->getPosition() );
     614           0 :         basegfx::B2IVector aNewPos = basegfx::B2IVector( aOldPos.X, aOldPos.Y ) + nShift*aTangentialDirection;
     615             : 
     616             :         //check whether the new position is ok
     617           0 :         awt::Point aNewAWTPos( aNewPos.getX(), aNewPos.getY() );
     618           0 :         if( !lcl_isInsidePage( aNewAWTPos, this->xLabelGroupShape->getSize(), rPageSize ) )
     619           0 :             return false;
     620             : 
     621           0 :         this->xLabelGroupShape->setPosition( aNewAWTPos );
     622           0 :         this->bMoved = true;
     623             :     }
     624           0 :     return true;
     625             : }
     626             : 
     627           0 : void PieChart::resetLabelPositionsToPreviousState()
     628             : {
     629           0 :     std::vector< PieLabelInfo >::iterator aIt = m_aLabelInfoList.begin();
     630           0 :     std::vector< PieLabelInfo >::const_iterator aEnd = m_aLabelInfoList.end();
     631           0 :     for( ;aIt!=aEnd; ++aIt )
     632           0 :         aIt->xLabelGroupShape->setPosition(aIt->aPreviousPosition);
     633           0 : }
     634             : 
     635           0 : bool PieChart::detectLabelOverlapsAndMove( const awt::Size& rPageSize )
     636             : {
     637             :     //returns true when there might be more to do
     638             : 
     639             :     //find borders of a group of overlapping labels
     640           0 :     bool bOverlapFound = false;
     641           0 :     PieLabelInfo* pStart = &(*(m_aLabelInfoList.rbegin()));
     642           0 :     PieLabelInfo* pFirstBorder = 0;
     643           0 :     PieLabelInfo* pSecondBorder = 0;
     644           0 :     PieLabelInfo* pCurrent = pStart;
     645           0 :     do
     646             :     {
     647           0 :         ::basegfx::B2IRectangle aPreviousOverlap( lcl_getRect( pCurrent->xLabelGroupShape ) );
     648           0 :         ::basegfx::B2IRectangle aNextOverlap( aPreviousOverlap );
     649           0 :         aPreviousOverlap.intersect( lcl_getRect( pCurrent->pPrevious->xLabelGroupShape ) );
     650           0 :         aNextOverlap.intersect( lcl_getRect( pCurrent->pNext->xLabelGroupShape ) );
     651             : 
     652           0 :         bool bPreviousOverlap = !aPreviousOverlap.isEmpty();
     653           0 :         bool bNextOverlap = !aNextOverlap.isEmpty();
     654           0 :         if( bPreviousOverlap || bNextOverlap )
     655           0 :             bOverlapFound = true;
     656           0 :         if( !bPreviousOverlap && bNextOverlap )
     657             :         {
     658           0 :             pFirstBorder = pCurrent;
     659             :             break;
     660             :         }
     661           0 :         pCurrent = pCurrent->pNext;
     662             :     }
     663             :     while( pCurrent != pStart );
     664             : 
     665           0 :     if( !bOverlapFound )
     666           0 :         return false;
     667             : 
     668           0 :     if( pFirstBorder )
     669             :     {
     670           0 :         pCurrent = pFirstBorder;
     671           0 :         do
     672             :         {
     673           0 :             ::basegfx::B2IRectangle aPreviousOverlap( lcl_getRect( pCurrent->xLabelGroupShape ) );
     674           0 :             ::basegfx::B2IRectangle aNextOverlap( aPreviousOverlap );
     675           0 :             aPreviousOverlap.intersect( lcl_getRect( pCurrent->pPrevious->xLabelGroupShape ) );
     676           0 :             aNextOverlap.intersect( lcl_getRect( pCurrent->pNext->xLabelGroupShape ) );
     677             : 
     678           0 :             if( !aPreviousOverlap.isEmpty() && aNextOverlap.isEmpty() )
     679             :             {
     680           0 :                 pSecondBorder = pCurrent;
     681             :                 break;
     682             :             }
     683           0 :             pCurrent = pCurrent->pNext;
     684             :         }
     685             :         while( pCurrent != pFirstBorder );
     686             :     }
     687             : 
     688           0 :     if( !pFirstBorder || !pSecondBorder )
     689             :     {
     690           0 :         pFirstBorder = &(*(m_aLabelInfoList.rbegin()));
     691           0 :         pSecondBorder = &(*(m_aLabelInfoList.begin()));
     692             :     }
     693             : 
     694             :     //find center
     695           0 :     PieLabelInfo* pCenter = pFirstBorder;
     696           0 :     sal_Int32 nOverlapGroupCount = 1;
     697           0 :     for( pCurrent = pFirstBorder ;pCurrent != pSecondBorder; pCurrent = pCurrent->pNext )
     698           0 :         nOverlapGroupCount++;
     699           0 :     sal_Int32 nCenterPos = nOverlapGroupCount/2;
     700           0 :     bool bSingleCenter = nOverlapGroupCount%2 != 0;
     701           0 :     if( bSingleCenter )
     702           0 :         nCenterPos++;
     703           0 :     if(nCenterPos>1)
     704             :     {
     705           0 :         pCurrent = pFirstBorder;
     706           0 :         while( --nCenterPos )
     707           0 :             pCurrent = pCurrent->pNext;
     708           0 :         pCenter = pCurrent;
     709             :     }
     710             : 
     711             :     //remind current positions
     712           0 :     pCurrent = pStart;
     713           0 :     do
     714             :     {
     715           0 :         pCurrent->aPreviousPosition = pCurrent->xLabelGroupShape->getPosition();
     716           0 :         pCurrent = pCurrent->pNext;
     717             :     }
     718             :     while( pCurrent != pStart );
     719             : 
     720             :     //
     721           0 :     bool bAlternativeMoveDirection = false;
     722           0 :     if( !tryMoveLabels( pFirstBorder, pSecondBorder, pCenter, bSingleCenter, bAlternativeMoveDirection, rPageSize ) )
     723           0 :         tryMoveLabels( pFirstBorder, pSecondBorder, pCenter, bSingleCenter, bAlternativeMoveDirection, rPageSize );
     724           0 :     return true;
     725             : }
     726             : 
     727           0 : bool PieChart::tryMoveLabels( PieLabelInfo* pFirstBorder, PieLabelInfo* pSecondBorder
     728             :                              , PieLabelInfo* pCenter
     729             :                              , bool bSingleCenter, bool& rbAlternativeMoveDirection, const awt::Size& rPageSize )
     730             : {
     731           0 :     PieLabelInfo* p1 = bSingleCenter ? pCenter->pPrevious : pCenter;
     732           0 :     PieLabelInfo* p2 = pCenter->pNext;
     733             :     //return true when successful
     734             : 
     735           0 :     bool bLabelOrderIsAntiClockWise = m_pPosHelper->isMathematicalOrientationAngle();
     736             : 
     737           0 :     PieLabelInfo* pCurrent = 0;
     738           0 :     for( pCurrent = p2 ;pCurrent->pPrevious != pSecondBorder; pCurrent = pCurrent->pNext )
     739             :     {
     740           0 :         PieLabelInfo* pFix = 0;
     741           0 :         for( pFix = p2->pPrevious ;pFix != pCurrent; pFix = pFix->pNext )
     742             :         {
     743           0 :             if( !pCurrent->moveAwayFrom( pFix, rPageSize, !bSingleCenter && pCurrent == p2, !bLabelOrderIsAntiClockWise, rbAlternativeMoveDirection ) )
     744             :             {
     745           0 :                 if( !rbAlternativeMoveDirection )
     746             :                 {
     747           0 :                     rbAlternativeMoveDirection = true;
     748           0 :                     resetLabelPositionsToPreviousState();
     749           0 :                     return false;
     750             :                 }
     751             :             }
     752             :         }
     753             :     }
     754           0 :     for( pCurrent = p1 ;pCurrent->pNext != pFirstBorder; pCurrent = pCurrent->pPrevious )
     755             :     {
     756           0 :         PieLabelInfo* pFix = 0;
     757           0 :         for( pFix = p2->pNext ;pFix != pCurrent; pFix = pFix->pPrevious )
     758             :         {
     759           0 :             if( !pCurrent->moveAwayFrom( pFix, rPageSize, false, bLabelOrderIsAntiClockWise, rbAlternativeMoveDirection ) )
     760             :             {
     761           0 :                 if( !rbAlternativeMoveDirection )
     762             :                 {
     763           0 :                     rbAlternativeMoveDirection = true;
     764           0 :                     resetLabelPositionsToPreviousState();
     765           0 :                     return false;
     766             :                 }
     767             :             }
     768             :         }
     769             :     }
     770           0 :     return true;
     771             : }
     772             : 
     773           0 : void PieChart::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& rPageSize )
     774             : {
     775             :     //------------------------------------------------------------------
     776             :     //check whether there are any labels that should be moved
     777           0 :     std::vector< PieLabelInfo >::iterator aIt1 = m_aLabelInfoList.begin();
     778           0 :     std::vector< PieLabelInfo >::const_iterator aEnd = m_aLabelInfoList.end();
     779           0 :     bool bMoveableFound = false;
     780           0 :     for( ;aIt1!=aEnd; ++aIt1 )
     781             :     {
     782           0 :         if(aIt1->bMovementAllowed)
     783             :         {
     784           0 :             bMoveableFound = true;
     785           0 :             break;
     786             :         }
     787             :     }
     788           0 :     if(!bMoveableFound)
     789             :         return;
     790             : 
     791           0 :     double fPageDiagonaleLength = sqrt( double( rPageSize.Width*rPageSize.Width + rPageSize.Height*rPageSize.Height) );
     792           0 :     if( ::rtl::math::approxEqual( fPageDiagonaleLength, 0.0 ) )
     793             :         return;
     794             : 
     795             :     //------------------------------------------------------------------
     796             :     //init next and previous
     797           0 :     aIt1 = m_aLabelInfoList.begin();
     798           0 :     std::vector< PieLabelInfo >::iterator aIt2 = aIt1;
     799           0 :     if( aIt1==aEnd )//no need to do anything when we only have one label
     800             :         return;
     801           0 :     aIt1->pPrevious = &(*(m_aLabelInfoList.rbegin()));
     802           0 :     ++aIt2;
     803           0 :     for( ;aIt2!=aEnd; ++aIt1, ++aIt2 )
     804             :     {
     805           0 :         PieLabelInfo& rInfo1( *aIt1 );
     806           0 :         PieLabelInfo& rInfo2( *aIt2 );
     807           0 :         rInfo1.pNext = &rInfo2;
     808           0 :         rInfo2.pPrevious = &rInfo1;
     809             :     }
     810           0 :     aIt1->pNext = &(*(m_aLabelInfoList.begin()));
     811             : 
     812             : 
     813             :     //------------------------------------------------------------------
     814             :     //detect overlaps and move
     815           0 :     sal_Int32 nMaxIterations = 50;
     816           0 :     while( detectLabelOverlapsAndMove( rPageSize ) && nMaxIterations > 0 )
     817           0 :         nMaxIterations--;
     818             : 
     819             :     //------------------------------------------------------------------
     820             :     //create connection lines for the moved labels
     821           0 :     aEnd = m_aLabelInfoList.end();
     822           0 :     VLineProperties aVLineProperties;
     823           0 :     for( aIt1 = m_aLabelInfoList.begin(); aIt1!=aEnd; ++aIt1 )
     824             :     {
     825           0 :         PieLabelInfo& rInfo( *aIt1 );
     826           0 :         if( rInfo.bMoved )
     827             :         {
     828           0 :             sal_Int32 nX1 = rInfo.aFirstPosition.getX();
     829           0 :             sal_Int32 nY1 = rInfo.aFirstPosition.getY();
     830           0 :             sal_Int32 nX2 = nX1;
     831           0 :             sal_Int32 nY2 = nY1;
     832           0 :             ::basegfx::B2IRectangle aRect( lcl_getRect( rInfo.xLabelGroupShape ) );
     833           0 :             if( nX1 < aRect.getMinX() )
     834           0 :                 nX2 = aRect.getMinX();
     835           0 :             else if( nX1 > aRect.getMaxX() )
     836           0 :                 nX2 = aRect.getMaxX();
     837             : 
     838           0 :             if( nY1 < aRect.getMinY() )
     839           0 :                 nY2 = aRect.getMinY();
     840           0 :             else if( nY1 > aRect.getMaxY() )
     841           0 :                 nY2 = aRect.getMaxY();
     842             : 
     843             : 
     844             :             //when the line is very short compared to the page size don't create one
     845           0 :             ::basegfx::B2DVector aLength(nX1-nX2, nY1-nY2);
     846           0 :             if( (aLength.getLength()/fPageDiagonaleLength) < 0.01 )
     847           0 :                 continue;
     848             : 
     849           0 :             drawing::PointSequenceSequence aPoints(1);
     850           0 :             aPoints[0].realloc(2);
     851           0 :             aPoints[0][0].X = nX1;
     852           0 :             aPoints[0][0].Y = nY1;
     853           0 :             aPoints[0][1].X = nX2;
     854           0 :             aPoints[0][1].Y = nY2;
     855             : 
     856           0 :             uno::Reference< beans::XPropertySet > xProp( rInfo.xTextShape, uno::UNO_QUERY);
     857           0 :             if( xProp.is() )
     858             :             {
     859           0 :                 sal_Int32 nColor = 0;
     860           0 :                 xProp->getPropertyValue(C2U("CharColor")) >>= nColor;
     861           0 :                 if( nColor != -1 )//automatic font color does not work for lines -> fallback to black
     862           0 :                     aVLineProperties.Color = uno::makeAny(nColor);
     863             :             }
     864           0 :             m_pShapeFactory->createLine2D( rInfo.xTextTarget, aPoints, &aVLineProperties );
     865             :         }
     866           0 :     }
     867             : }
     868             : 
     869             : //.............................................................................
     870             : } //namespace chart
     871             : //.............................................................................
     872             : 
     873             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10