1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: PieChart.cxx,v $
10 * $Revision: 1.20.44.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_chart2.hxx"
35 #include "PieChart.hxx"
36 #include "PlottingPositionHelper.hxx"
37 #include "ShapeFactory.hxx"
38 #include "PolarLabelPositionHelper.hxx"
40 #include "CommonConverters.hxx"
41 #include "ViewDefines.hxx"
42 #include "ObjectIdentifier.hxx"
44 #include <com/sun/star/chart/DataLabelPlacement.hpp>
45 #include <com/sun/star/chart2/XColorScheme.hpp>
47 #include <com/sun/star/container/XChild.hpp>
49 //#include "chartview/servicenames_charttypes.hxx"
50 //#include "servicenames_coosystems.hxx"
51 #include <tools/debug.hxx>
52 #include <rtl/math.hxx>
54 //.............................................................................
57 //.............................................................................
58 using namespace ::com::sun::star
;
59 using namespace ::com::sun::star::chart2
;
61 class PiePositionHelper
: public PolarPlottingPositionHelper
64 PiePositionHelper( NormalAxis eNormalAxis
, double fAngleDegreeOffset
);
65 virtual ~PiePositionHelper();
67 bool getInnerAndOuterRadius( double fCategoryX
, double& fLogicInnerRadius
, double& fLogicOuterRadius
, bool bUseRings
, double fMaxOffset
) const;
70 //Distance between different category rings, seen relative to width of a ring:
71 double m_fRingDistance
; //>=0 m_fRingDistance=1 --> distance == width
74 PiePositionHelper::PiePositionHelper( NormalAxis eNormalAxis
, double fAngleDegreeOffset
)
75 : PolarPlottingPositionHelper(eNormalAxis
)
76 , m_fRingDistance(0.0)
78 m_fRadiusOffset
= 0.0;
79 m_fAngleDegreeOffset
= fAngleDegreeOffset
;
82 PiePositionHelper::~PiePositionHelper()
86 bool PiePositionHelper::getInnerAndOuterRadius( double fCategoryX
87 , double& fLogicInnerRadius
, double& fLogicOuterRadius
88 , bool bUseRings
, double fMaxOffset
) const
93 bool bIsVisible
= true;
94 double fLogicInner
= fCategoryX
-0.5+m_fRingDistance
/2.0;
95 double fLogicOuter
= fCategoryX
+0.5-m_fRingDistance
/2.0;
97 if( !isMathematicalOrientationRadius() )
99 //in this case the given getMaximumX() was not corrcect instead the minimum should have been smaller by fMaxOffset
100 //but during getMaximumX and getMimumX we do not know the axis orientation
101 fLogicInner
+= fMaxOffset
;
102 fLogicOuter
+= fMaxOffset
;
105 if( fLogicInner
>= getLogicMaxX() )
107 if( fLogicOuter
<= getLogicMinX() )
110 if( fLogicInner
< getLogicMinX() )
111 fLogicInner
= getLogicMinX();
112 if( fLogicOuter
> getLogicMaxX() )
113 fLogicOuter
= getLogicMaxX();
115 fLogicInnerRadius
= fLogicInner
;
116 fLogicOuterRadius
= fLogicOuter
;
117 if( !isMathematicalOrientationRadius() )
118 std::swap(fLogicInnerRadius
,fLogicOuterRadius
);
122 //-----------------------------------------------------------------------------
123 //-----------------------------------------------------------------------------
124 //-----------------------------------------------------------------------------
126 PieChart::PieChart( const uno::Reference
<XChartType
>& xChartTypeModel
127 , sal_Int32 nDimensionCount
)
128 : VSeriesPlotter( xChartTypeModel
, nDimensionCount
)
129 , m_pPosHelper( new PiePositionHelper( NormalAxis_Z
, (m_nDimension
==3)?0.0:90.0 ) )
132 ::rtl::math::setNan(&m_fMaxOffset
);
134 PlotterBase::m_pPosHelper
= m_pPosHelper
;
135 VSeriesPlotter::m_pMainPosHelper
= m_pPosHelper
;
136 m_pPosHelper
->m_fRadiusOffset
= 0.0;
137 m_pPosHelper
->m_fRingDistance
= 0.0;
139 uno::Reference
< beans::XPropertySet
> xChartTypeProps( xChartTypeModel
, uno::UNO_QUERY
);
140 if( xChartTypeProps
.is() ) try
142 xChartTypeProps
->getPropertyValue( C2U( "UseRings" )) >>= m_bUseRings
;
145 m_pPosHelper
->m_fRadiusOffset
= 1.0;
146 if( nDimensionCount
==3 )
147 m_pPosHelper
->m_fRingDistance
= 0.1;
150 catch( uno::Exception
& e
)
152 ASSERT_EXCEPTION( e
);
156 PieChart::~PieChart()
161 //-----------------------------------------------------------------
163 void SAL_CALL
PieChart::setScales( const uno::Sequence
< ExplicitScaleData
>& rScales
164 , sal_Bool
/* bSwapXAndYAxis */ )
165 throw (uno::RuntimeException
)
167 DBG_ASSERT(m_nDimension
<=rScales
.getLength(),"Dimension of Plotter does not fit two dimension of given scale sequence");
168 m_pPosHelper
->setScales( rScales
, true );
171 //-----------------------------------------------------------------
173 drawing::Direction3D
PieChart::getPreferredDiagramAspectRatio() const
175 if( m_nDimension
== 3 )
176 return drawing::Direction3D(1,1,0.25);
177 return drawing::Direction3D(1,1,1);
180 bool PieChart::keepAspectRatio() const
182 if( m_nDimension
== 3 )
187 //-----------------------------------------------------------------
188 // lang::XServiceInfo
189 //-----------------------------------------------------------------
191 APPHELPER_XSERVICEINFO_IMPL(PieChart,CHART2_VIEW_PIECHART_SERVICE_IMPLEMENTATION_NAME)
193 uno::Sequence< rtl::OUString > PieChart
194 ::getSupportedServiceNames_Static()
196 uno::Sequence< rtl::OUString > aSNS( 1 );
197 aSNS.getArray()[ 0 ] = CHART2_VIEW_PIECHART_SERVICE_NAME;
202 uno::Reference
< drawing::XShape
> PieChart::createDataPoint(
203 const uno::Reference
< drawing::XShapes
>& xTarget
204 , const uno::Reference
< beans::XPropertySet
>& xObjectProperties
205 , double fUnitCircleStartAngleDegree
, double fUnitCircleWidthAngleDegree
206 , double fUnitCircleInnerRadius
, double fUnitCircleOuterRadius
207 , double fLogicZ
, double fDepth
, double fExplodePercentage
208 , tPropertyNameValueMap
* pOverwritePropertiesMap
)
210 //---------------------------
211 //transform position:
212 drawing::Direction3D aOffset
;
213 if( !::rtl::math::approxEqual( fExplodePercentage
, 0.0 ) )
215 double fAngle
= fUnitCircleStartAngleDegree
+ fUnitCircleWidthAngleDegree
/2.0;
216 double fRadius
= (fUnitCircleOuterRadius
-fUnitCircleInnerRadius
)*fExplodePercentage
;
217 drawing::Position3D aOrigin
= m_pPosHelper
->transformUnitCircleToScene( 0, 0, fLogicZ
);
218 drawing::Position3D aNewOrigin
= m_pPosHelper
->transformUnitCircleToScene( fAngle
, fRadius
, fLogicZ
);
219 aOffset
= aNewOrigin
- aOrigin
;
222 //---------------------------
224 uno::Reference
< drawing::XShape
> xShape(0);
227 xShape
= m_pShapeFactory
->createPieSegment( xTarget
228 , fUnitCircleStartAngleDegree
, fUnitCircleWidthAngleDegree
229 , fUnitCircleInnerRadius
, fUnitCircleOuterRadius
230 , aOffset
, B3DHomMatrixToHomogenMatrix( m_pPosHelper
->getUnitCartesianToScene() )
235 xShape
= m_pShapeFactory
->createPieSegment2D( xTarget
236 , fUnitCircleStartAngleDegree
, fUnitCircleWidthAngleDegree
237 , fUnitCircleInnerRadius
, fUnitCircleOuterRadius
238 , aOffset
, B3DHomMatrixToHomogenMatrix( m_pPosHelper
->getUnitCartesianToScene() ) );
240 this->setMappedProperties( xShape
, xObjectProperties
, PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), pOverwritePropertiesMap
);
244 void PieChart::addSeries( VDataSeries
* pSeries
, sal_Int32
/* zSlot */, sal_Int32
/* xSlot */, sal_Int32
/* ySlot */ )
246 VSeriesPlotter::addSeries( pSeries
, 0, -1, 0 );
249 double PieChart::getMinimumX()
253 double PieChart::getMaxOffset()
255 if (!::rtl::math::isNan(m_fMaxOffset
))
256 // Value already cached. Use it.
260 if( m_aZSlots
.size()<=0 )
262 if( m_aZSlots
[0].size()<=0 )
265 const ::std::vector
< VDataSeries
* >& rSeriesList( m_aZSlots
[0][0].m_aSeriesVector
);
266 if( rSeriesList
.size()<=0 )
269 VDataSeries
* pSeries
= rSeriesList
[0];
270 uno::Reference
< beans::XPropertySet
> xSeriesProp( pSeries
->getPropertiesOfSeries() );
271 if( !xSeriesProp
.is() )
274 double fExplodePercentage
=0.0;
275 xSeriesProp
->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage
;
276 if(fExplodePercentage
>m_fMaxOffset
)
277 m_fMaxOffset
=fExplodePercentage
;
279 uno::Sequence
< sal_Int32
> aAttributedDataPointIndexList
;
280 if( xSeriesProp
->getPropertyValue( C2U( "AttributedDataPoints" ) ) >>= aAttributedDataPointIndexList
)
282 for(sal_Int32 nN
=aAttributedDataPointIndexList
.getLength();nN
--;)
284 uno::Reference
< beans::XPropertySet
> xPointProp( pSeries
->getPropertiesOfPoint(aAttributedDataPointIndexList
[nN
]) );
287 fExplodePercentage
=0.0;
288 xPointProp
->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage
;
289 if(fExplodePercentage
>m_fMaxOffset
)
290 m_fMaxOffset
=fExplodePercentage
;
296 double PieChart::getMaximumX()
298 double fMaxOffset
= getMaxOffset();
299 if( m_aZSlots
.size()>0 && m_bUseRings
)
300 return m_aZSlots
[0].size()+0.5+fMaxOffset
;
301 return 1.5+fMaxOffset
;
303 double PieChart::getMinimumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32
/* nAxisIndex */ )
308 double PieChart::getMaximumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32
/* nAxisIndex */ )
313 bool PieChart::isExpandBorderToIncrementRhythm( sal_Int32
/* nDimensionIndex */ )
318 bool PieChart::isExpandIfValuesCloseToBorder( sal_Int32
/* nDimensionIndex */ )
323 bool PieChart::isExpandWideValuesToZero( sal_Int32
/* nDimensionIndex */ )
328 bool PieChart::isExpandNarrowValuesTowardZero( sal_Int32
/* nDimensionIndex */ )
333 bool PieChart::isSeperateStackingForDifferentSigns( sal_Int32
/* nDimensionIndex */ )
338 void PieChart::createShapes()
340 if( m_aZSlots
.begin() == m_aZSlots
.end() ) //no series
343 DBG_ASSERT(m_pShapeFactory
&&m_xLogicTarget
.is()&&m_xFinalTarget
.is(),"PieChart is not proper initialized");
344 if(!(m_pShapeFactory
&&m_xLogicTarget
.is()&&m_xFinalTarget
.is()))
347 //the text labels should be always on top of the other series shapes
348 //therefore create an own group for the texts to move them to front
349 //(because the text group is created after the series group the texts are displayed on top)
350 uno::Reference
< drawing::XShapes
> xSeriesTarget(
351 createGroupShape( m_xLogicTarget
,rtl::OUString() ));
352 uno::Reference
< drawing::XShapes
> xTextTarget(
353 m_pShapeFactory
->createGroup2D( m_xFinalTarget
,rtl::OUString() ));
354 //---------------------------------------------
355 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
357 //=============================================================================
358 ::std::vector
< VDataSeriesGroup
>::iterator aXSlotIter
= m_aZSlots
[0].begin();
359 const ::std::vector
< VDataSeriesGroup
>::const_iterator aXSlotEnd
= m_aZSlots
[0].end();
361 ::std::vector
< VDataSeriesGroup
>::size_type nExplodeableSlot
= 0;
362 if( m_pPosHelper
->isMathematicalOrientationRadius() && m_bUseRings
)
363 nExplodeableSlot
= m_aZSlots
[0].size()-1;
365 m_aLabelInfoList
.clear();
366 ::rtl::math::setNan(&m_fMaxOffset
);
368 //=============================================================================
369 for( double fSlotX
=0; aXSlotIter
!= aXSlotEnd
&& (m_bUseRings
||fSlotX
<0.5 ); aXSlotIter
++, fSlotX
+=1.0 )
371 ::std::vector
< VDataSeries
* >* pSeriesList
= &(aXSlotIter
->m_aSeriesVector
);
372 if( pSeriesList
->size()<=0 )//there should be only one series in each x slot
374 VDataSeries
* pSeries
= (*pSeriesList
)[0];
378 m_pPosHelper
->m_fAngleDegreeOffset
= pSeries
->getStartingAngle();
380 double fLogicYSum
= 0.0;
381 //iterate through all points to get the sum
382 sal_Int32 nPointIndex
=0;
383 sal_Int32 nPointCount
=pSeries
->getTotalPointCount();
384 for( nPointIndex
= 0; nPointIndex
< nPointCount
; nPointIndex
++ )
386 double fY
= pSeries
->getYValue( nPointIndex
);
389 //@todo warn somehow that negative values are treated as positive
391 if( ::rtl::math::isNan(fY
) )
393 fLogicYSum
+= fabs(fY
);
397 double fLogicYForNextPoint
= 0.0;
398 //iterate through all points to create shapes
399 for( nPointIndex
= 0; nPointIndex
< nPointCount
; nPointIndex
++ )
401 double fLogicInnerRadius
, fLogicOuterRadius
;
402 bool bIsVisible
= m_pPosHelper
->getInnerAndOuterRadius( fSlotX
+1.0, fLogicInnerRadius
, fLogicOuterRadius
, m_bUseRings
, getMaxOffset() );
406 double fLogicZ
= -0.5;//as defined
407 double fDepth
= this->getTransformedDepth();
408 //=============================================================================
410 uno::Reference
< drawing::XShapes
> xSeriesGroupShape_Shapes
= getSeriesGroupShape(pSeries
, xSeriesTarget
);
411 //collect data point information (logic coordinates, style ):
412 double fLogicYValue
= fabs(pSeries
->getYValue( nPointIndex
));
413 if( ::rtl::math::isNan(fLogicYValue
) )
415 if(fLogicYValue
==0.0)//@todo: continue also if the resolution to small
417 double fLogicYPos
= fLogicYForNextPoint
;
418 fLogicYForNextPoint
+= fLogicYValue
;
420 uno::Reference
< beans::XPropertySet
> xPointProperties
= pSeries
->getPropertiesOfPoint( nPointIndex
);
422 //iterate through all subsystems to create partial points
424 //logic values on angle axis:
425 double fLogicStartAngleValue
= fLogicYPos
/fLogicYSum
;
426 double fLogicEndAngleValue
= (fLogicYPos
+fLogicYValue
)/fLogicYSum
;
428 double fExplodePercentage
=0.0;
429 bool bDoExplode
= ( nExplodeableSlot
== static_cast< ::std::vector
< VDataSeriesGroup
>::size_type
>(fSlotX
) );
432 xPointProperties
->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage
;
434 catch( uno::Exception
& e
)
436 ASSERT_EXCEPTION( e
);
439 //---------------------------
440 //transforme to unit circle:
441 double fUnitCircleWidthAngleDegree
= m_pPosHelper
->getWidthAngleDegree( fLogicStartAngleValue
, fLogicEndAngleValue
);
442 double fUnitCircleStartAngleDegree
= m_pPosHelper
->transformToAngleDegree( fLogicStartAngleValue
);
443 double fUnitCircleInnerRadius
= m_pPosHelper
->transformToRadius( fLogicInnerRadius
);
444 double fUnitCircleOuterRadius
= m_pPosHelper
->transformToRadius( fLogicOuterRadius
);
446 //---------------------------
448 std::auto_ptr
< tPropertyNameValueMap
> apOverwritePropertiesMap(0);
450 if(!pSeries
->hasPointOwnColor(nPointIndex
) && m_xColorScheme
.is())
452 apOverwritePropertiesMap
= std::auto_ptr
< tPropertyNameValueMap
>( new tPropertyNameValueMap() );
453 (*apOverwritePropertiesMap
)[C2U("FillColor")] = uno::makeAny(
454 m_xColorScheme
->getColorByIndex( nPointIndex
));
459 uno::Reference
<drawing::XShape
> xPointShape(
460 createDataPoint( xSeriesGroupShape_Shapes
, xPointProperties
461 , fUnitCircleStartAngleDegree
, fUnitCircleWidthAngleDegree
462 , fUnitCircleInnerRadius
, fUnitCircleOuterRadius
463 , fLogicZ
, fDepth
, fExplodePercentage
, apOverwritePropertiesMap
.get() ) );
466 if( pSeries
->getDataPointLabelIfLabel(nPointIndex
) )
468 if( !::rtl::math::approxEqual( fExplodePercentage
, 0.0 ) )
470 double fExplodeOffset
= (fUnitCircleOuterRadius
-fUnitCircleInnerRadius
)*fExplodePercentage
;
471 fUnitCircleInnerRadius
+= fExplodeOffset
;
472 fUnitCircleOuterRadius
+= fExplodeOffset
;
475 sal_Int32 nLabelPlacement
= pSeries
->getLabelPlacement( nPointIndex
, m_xChartTypeModel
, m_nDimension
, m_pPosHelper
->isSwapXAndY() );
476 bool bMovementAllowed
= ( nLabelPlacement
== ::com::sun::star::chart::DataLabelPlacement::AVOID_OVERLAP
);
477 if( bMovementAllowed
)
478 nLabelPlacement
= ::com::sun::star::chart::DataLabelPlacement::OUTSIDE
;
480 LabelAlignment
eAlignment(LABEL_ALIGN_CENTER
);
481 sal_Int32 nScreenValueOffsetInRadiusDirection
= 0 ;
482 if( nLabelPlacement
== ::com::sun::star::chart::DataLabelPlacement::OUTSIDE
)
483 nScreenValueOffsetInRadiusDirection
= (3!=m_nDimension
) ? 150 : 0;//todo maybe calculate this font height dependent
484 else if( nLabelPlacement
== ::com::sun::star::chart::DataLabelPlacement::INSIDE
)
485 nScreenValueOffsetInRadiusDirection
= (3!=m_nDimension
) ? -150 : 0;//todo maybe calculate this font height dependent
486 PolarLabelPositionHelper
aPolarPosHelper(m_pPosHelper
,m_nDimension
,m_xLogicTarget
,m_pShapeFactory
);
487 awt::Point
aScreenPosition2D(
488 aPolarPosHelper
.getLabelScreenPositionAndAlignmentForUnitCircleValues(eAlignment
, nLabelPlacement
489 , fUnitCircleStartAngleDegree
, fUnitCircleWidthAngleDegree
490 , fUnitCircleInnerRadius
, fUnitCircleOuterRadius
, 0.0, 0 ));
492 PieLabelInfo aPieLabelInfo
;
493 aPieLabelInfo
.aFirstPosition
= basegfx::B2IVector( aScreenPosition2D
.X
, aScreenPosition2D
.Y
);
494 awt::Point
aOrigin( aPolarPosHelper
.transformSceneToScreenPosition( m_pPosHelper
->transformUnitCircleToScene( 0.0, 0.0, 0.5 ) ) );
495 aPieLabelInfo
.aOrigin
= basegfx::B2IVector( aOrigin
.X
, aOrigin
.Y
);
497 //add a scaling independent Offset if requested
498 if( nScreenValueOffsetInRadiusDirection
!= 0)
500 basegfx::B2IVector
aDirection( aScreenPosition2D
.X
- aOrigin
.X
, aScreenPosition2D
.Y
- aOrigin
.Y
);
501 aDirection
.setLength(nScreenValueOffsetInRadiusDirection
);
502 aScreenPosition2D
.X
+= aDirection
.getX();
503 aScreenPosition2D
.Y
+= aDirection
.getY();
506 aPieLabelInfo
.xTextShape
= this->createDataLabel( xTextTarget
, *pSeries
, nPointIndex
507 , fLogicYValue
, fLogicYSum
, aScreenPosition2D
, eAlignment
);
509 uno::Reference
< container::XChild
> xChild( aPieLabelInfo
.xTextShape
, uno::UNO_QUERY
);
511 aPieLabelInfo
.xLabelGroupShape
= uno::Reference
<drawing::XShape
>( xChild
->getParent(), uno::UNO_QUERY
);
512 aPieLabelInfo
.fValue
= fLogicYValue
;
513 aPieLabelInfo
.bMovementAllowed
= bMovementAllowed
;
514 aPieLabelInfo
.bMoved
= false;
515 aPieLabelInfo
.xTextTarget
= xTextTarget
;
516 m_aLabelInfoList
.push_back(aPieLabelInfo
);
521 ShapeFactory::setShapeName( xPointShape
522 , ObjectIdentifier::createPointCID( pSeries
->getPointCID_Stub(), nPointIndex
) );
526 //enable dragging of outer segments
528 double fAngle
= fUnitCircleStartAngleDegree
+ fUnitCircleWidthAngleDegree
/2.0;
529 double fMaxDeltaRadius
= fUnitCircleOuterRadius
-fUnitCircleInnerRadius
;
530 drawing::Position3D aOrigin
= m_pPosHelper
->transformUnitCircleToScene( fAngle
, fUnitCircleOuterRadius
, fLogicZ
);
531 drawing::Position3D aNewOrigin
= m_pPosHelper
->transformUnitCircleToScene( fAngle
, fUnitCircleOuterRadius
+ fMaxDeltaRadius
, fLogicZ
);
533 sal_Int32
nOffsetPercent( static_cast<sal_Int32
>(fExplodePercentage
* 100.0) );
535 awt::Point
aMinimumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
536 aOrigin
, m_xLogicTarget
, m_pShapeFactory
, m_nDimension
) );
537 awt::Point
aMaximumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
538 aNewOrigin
, m_xLogicTarget
, m_pShapeFactory
, m_nDimension
) );
540 //enable draging of piesegments
541 rtl::OUString
aPointCIDStub( ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT
542 , pSeries
->getSeriesParticle()
543 , ObjectIdentifier::getPieSegmentDragMethodServiceName()
544 , ObjectIdentifier::createPieSegmentDragParameterString(
545 nOffsetPercent
, aMinimumPosition
, aMaximumPosition
)
548 ShapeFactory::setShapeName( xPointShape
549 , ObjectIdentifier::createPointCID( aPointCIDStub
, nPointIndex
) );
551 catch( uno::Exception
& e
)
553 ASSERT_EXCEPTION( e
);
555 }//next series in x slot (next y slot)
558 //=============================================================================
559 //=============================================================================
560 //=============================================================================
561 /* @todo remove series shapes if empty
562 //remove and delete point-group-shape if empty
563 if(!xSeriesGroupShape_Shapes->getCount())
565 (*aSeriesIter)->m_xShape.set(NULL);
566 m_xLogicTarget->remove(xSeriesGroupShape_Shape);
570 //remove and delete series-group-shape if empty
578 ::basegfx::B2IRectangle
lcl_getRect( const uno::Reference
< drawing::XShape
>& xShape
)
580 ::basegfx::B2IRectangle aRect
;
582 aRect
= BaseGFXHelper::makeRectangle(xShape
->getPosition(),xShape
->getSize() );
586 bool lcl_isInsidePage( const awt::Point
& rPos
, const awt::Size
& rSize
, const awt::Size
& rPageSize
)
588 if( rPos
.X
< 0 || rPos
.Y
< 0 )
590 if( (rPos
.X
+ rSize
.Width
) > rPageSize
.Width
)
592 if( (rPos
.Y
+ rSize
.Height
) > rPageSize
.Height
)
597 }//end anonymous namespace
599 PieChart::PieLabelInfo::PieLabelInfo()
600 : xTextShape(0), xLabelGroupShape(0), aFirstPosition(), aOrigin(), fValue(0.0)
601 , bMovementAllowed(false), bMoved(false), xTextTarget(0), pPrevious(0),pNext(0)
605 bool PieChart::PieLabelInfo::moveAwayFrom( const PieChart::PieLabelInfo
* pFix
, const awt::Size
& rPageSize
, bool bMoveHalfWay
, bool bMoveClockwise
, bool bAlternativeMoveDirection
)
607 //return true if the move was successful
608 if(!this->bMovementAllowed
)
611 const sal_Int32 nLabelDistanceX
= rPageSize
.Width
/50;
612 const sal_Int32 nLabelDistanceY
= rPageSize
.Height
/50;
614 ::basegfx::B2IRectangle
aOverlap( lcl_getRect( this->xLabelGroupShape
) );
615 aOverlap
.intersect( lcl_getRect( pFix
->xLabelGroupShape
) );
616 if( !aOverlap
.isEmpty() )
618 (void)bAlternativeMoveDirection
;//todo
620 basegfx::B2IVector aRadiusDirection
= this->aFirstPosition
- this->aOrigin
;
621 aRadiusDirection
.setLength(1.0);
622 basegfx::B2IVector
aTangentialDirection( -aRadiusDirection
.getY(), aRadiusDirection
.getX() );
623 bool bShiftHorizontal
= abs(aTangentialDirection
.getX()) > abs(aTangentialDirection
.getY());
625 sal_Int32 nShift
= bShiftHorizontal
? static_cast<sal_Int32
>(aOverlap
.getWidth()) : static_cast<sal_Int32
>(aOverlap
.getHeight());
626 nShift
+= (bShiftHorizontal
? nLabelDistanceX
: nLabelDistanceY
);
631 awt::Point
aOldPos( this->xLabelGroupShape
->getPosition() );
632 basegfx::B2IVector aNewPos
= basegfx::B2IVector( aOldPos
.X
, aOldPos
.Y
) + nShift
*aTangentialDirection
;
634 //check whether the new position is ok
635 awt::Point
aNewAWTPos( aNewPos
.getX(), aNewPos
.getY() );
636 if( !lcl_isInsidePage( aNewAWTPos
, this->xLabelGroupShape
->getSize(), rPageSize
) )
639 this->xLabelGroupShape
->setPosition( aNewAWTPos
);
645 void PieChart::resetLabelPositionsToPreviousState()
647 std::vector
< PieLabelInfo
>::iterator aIt
= m_aLabelInfoList
.begin();
648 std::vector
< PieLabelInfo
>::const_iterator aEnd
= m_aLabelInfoList
.end();
649 for( ;aIt
!=aEnd
; ++aIt
)
650 aIt
->xLabelGroupShape
->setPosition(aIt
->aPreviousPosition
);
653 bool PieChart::detectLabelOverlapsAndMove( const awt::Size
& rPageSize
)
655 //returns true when there might be more to do
657 //find borders of a group of overlapping labels
658 bool bOverlapFound
= false;
659 PieLabelInfo
* pStart
= &(*(m_aLabelInfoList
.rbegin()));
660 PieLabelInfo
* pFirstBorder
= 0;
661 PieLabelInfo
* pSecondBorder
= 0;
662 PieLabelInfo
* pCurrent
= pStart
;
665 ::basegfx::B2IRectangle
aPreviousOverlap( lcl_getRect( pCurrent
->xLabelGroupShape
) );
666 ::basegfx::B2IRectangle
aNextOverlap( aPreviousOverlap
);
667 aPreviousOverlap
.intersect( lcl_getRect( pCurrent
->pPrevious
->xLabelGroupShape
) );
668 aNextOverlap
.intersect( lcl_getRect( pCurrent
->pNext
->xLabelGroupShape
) );
670 bool bPreviousOverlap
= !aPreviousOverlap
.isEmpty();
671 bool bNextOverlap
= !aNextOverlap
.isEmpty();
672 if( bPreviousOverlap
|| bNextOverlap
)
673 bOverlapFound
= true;
674 if( !bPreviousOverlap
&& bNextOverlap
)
676 pFirstBorder
= pCurrent
;
679 pCurrent
= pCurrent
->pNext
;
681 while( pCurrent
!= pStart
);
688 pCurrent
= pFirstBorder
;
691 ::basegfx::B2IRectangle
aPreviousOverlap( lcl_getRect( pCurrent
->xLabelGroupShape
) );
692 ::basegfx::B2IRectangle
aNextOverlap( aPreviousOverlap
);
693 aPreviousOverlap
.intersect( lcl_getRect( pCurrent
->pPrevious
->xLabelGroupShape
) );
694 aNextOverlap
.intersect( lcl_getRect( pCurrent
->pNext
->xLabelGroupShape
) );
696 if( !aPreviousOverlap
.isEmpty() && aNextOverlap
.isEmpty() )
698 pSecondBorder
= pCurrent
;
701 pCurrent
= pCurrent
->pNext
;
703 while( pCurrent
!= pFirstBorder
);
706 if( !pFirstBorder
|| !pSecondBorder
)
708 pFirstBorder
= &(*(m_aLabelInfoList
.rbegin()));
709 pSecondBorder
= &(*(m_aLabelInfoList
.begin()));
713 PieLabelInfo
* pCenter
= pFirstBorder
;
714 sal_Int32 nOverlapGroupCount
= 1;
715 for( pCurrent
= pFirstBorder
;pCurrent
!= pSecondBorder
; pCurrent
= pCurrent
->pNext
)
716 nOverlapGroupCount
++;
717 sal_Int32 nCenterPos
= nOverlapGroupCount
/2;
718 bool bSingleCenter
= nOverlapGroupCount
%2 != 0;
723 pCurrent
= pFirstBorder
;
724 while( --nCenterPos
)
725 pCurrent
= pCurrent
->pNext
;
729 //remind current positions
733 pCurrent
->aPreviousPosition
= pCurrent
->xLabelGroupShape
->getPosition();
734 pCurrent
= pCurrent
->pNext
;
736 while( pCurrent
!= pStart
);
739 bool bAlternativeMoveDirection
= false;
740 if( !tryMoveLabels( pFirstBorder
, pSecondBorder
, pCenter
, bSingleCenter
, bAlternativeMoveDirection
, rPageSize
) )
741 tryMoveLabels( pFirstBorder
, pSecondBorder
, pCenter
, bSingleCenter
, bAlternativeMoveDirection
, rPageSize
);
745 bool PieChart::tryMoveLabels( PieLabelInfo
* pFirstBorder
, PieLabelInfo
* pSecondBorder
746 , PieLabelInfo
* pCenter
747 , bool bSingleCenter
, bool& rbAlternativeMoveDirection
, const awt::Size
& rPageSize
)
749 PieLabelInfo
* p1
= bSingleCenter
? pCenter
->pPrevious
: pCenter
;
750 PieLabelInfo
* p2
= pCenter
->pNext
;
751 //return true when successful
753 bool bLabelOrderIsAntiClockWise
= m_pPosHelper
->isMathematicalOrientationAngle();
755 PieLabelInfo
* pCurrent
= 0;
756 for( pCurrent
= p2
;pCurrent
->pPrevious
!= pSecondBorder
; pCurrent
= pCurrent
->pNext
)
758 PieLabelInfo
* pFix
= 0;
759 for( pFix
= p2
->pPrevious
;pFix
!= pCurrent
; pFix
= pFix
->pNext
)
761 if( !pCurrent
->moveAwayFrom( pFix
, rPageSize
, !bSingleCenter
&& pCurrent
== p2
, !bLabelOrderIsAntiClockWise
, rbAlternativeMoveDirection
) )
763 if( !rbAlternativeMoveDirection
)
765 rbAlternativeMoveDirection
= true;
766 resetLabelPositionsToPreviousState();
772 for( pCurrent
= p1
;pCurrent
->pNext
!= pFirstBorder
; pCurrent
= pCurrent
->pPrevious
)
774 PieLabelInfo
* pFix
= 0;
775 for( pFix
= p2
->pNext
;pFix
!= pCurrent
; pFix
= pFix
->pPrevious
)
777 if( !pCurrent
->moveAwayFrom( pFix
, rPageSize
, false, bLabelOrderIsAntiClockWise
, rbAlternativeMoveDirection
) )
779 if( !rbAlternativeMoveDirection
)
781 rbAlternativeMoveDirection
= true;
782 resetLabelPositionsToPreviousState();
791 void PieChart::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size
& rPageSize
)
793 //------------------------------------------------------------------
794 //check whether there are any labels that should be moved
795 std::vector
< PieLabelInfo
>::iterator aIt1
= m_aLabelInfoList
.begin();
796 std::vector
< PieLabelInfo
>::const_iterator aEnd
= m_aLabelInfoList
.end();
797 bool bMoveableFound
= false;
798 for( ;aIt1
!=aEnd
; ++aIt1
)
800 if(aIt1
->bMovementAllowed
)
802 bMoveableFound
= true;
809 double fPageDiagonaleLength
= sqrt( double( rPageSize
.Width
*rPageSize
.Width
+ rPageSize
.Height
*rPageSize
.Height
) );
810 if( ::rtl::math::approxEqual( fPageDiagonaleLength
, 0.0 ) )
813 //------------------------------------------------------------------
814 //init next and previous
815 aIt1
= m_aLabelInfoList
.begin();
816 std::vector
< PieLabelInfo
>::iterator aIt2
= aIt1
;
817 if( aIt1
==aEnd
)//no need to do anything when we only have one label
819 aIt1
->pPrevious
= &(*(m_aLabelInfoList
.rbegin()));
821 for( ;aIt2
!=aEnd
; ++aIt1
, ++aIt2
)
823 PieLabelInfo
& rInfo1( *aIt1
);
824 PieLabelInfo
& rInfo2( *aIt2
);
825 rInfo1
.pNext
= &rInfo2
;
826 rInfo2
.pPrevious
= &rInfo1
;
828 aIt1
->pNext
= &(*(m_aLabelInfoList
.begin()));
831 //------------------------------------------------------------------
832 //detect overlaps and move
833 sal_Int32 nMaxIterations
= 50;
834 while( detectLabelOverlapsAndMove( rPageSize
) && nMaxIterations
> 0 )
837 //------------------------------------------------------------------
838 //create connection lines for the moved labels
839 aEnd
= m_aLabelInfoList
.end();
840 VLineProperties aVLineProperties
;
841 for( aIt1
= m_aLabelInfoList
.begin(); aIt1
!=aEnd
; ++aIt1
)
843 PieLabelInfo
& rInfo( *aIt1
);
846 sal_Int32 nX1
= rInfo
.aFirstPosition
.getX();
847 sal_Int32 nY1
= rInfo
.aFirstPosition
.getY();
850 ::basegfx::B2IRectangle
aRect( lcl_getRect( rInfo
.xLabelGroupShape
) );
851 if( nX1
< aRect
.getMinX() )
852 nX2
= aRect
.getMinX();
853 else if( nX1
> aRect
.getMaxX() )
854 nX2
= aRect
.getMaxX();
856 if( nY1
< aRect
.getMinY() )
857 nY2
= aRect
.getMinY();
858 else if( nY1
> aRect
.getMaxY() )
859 nY2
= aRect
.getMaxY();
862 //when the line is very short compared to the page size don't create one
863 ::basegfx::B2DVector
aLength(nX1
-nX2
, nY1
-nY2
);
864 if( (aLength
.getLength()/fPageDiagonaleLength
) < 0.01 )
867 drawing::PointSequenceSequence
aPoints(1);
868 aPoints
[0].realloc(2);
869 aPoints
[0][0].X
= nX1
;
870 aPoints
[0][0].Y
= nY1
;
871 aPoints
[0][1].X
= nX2
;
872 aPoints
[0][1].Y
= nY2
;
874 uno::Reference
< beans::XPropertySet
> xProp( rInfo
.xTextShape
, uno::UNO_QUERY
);
877 sal_Int32 nColor
= 0;
878 xProp
->getPropertyValue(C2U("CharColor")) >>= nColor
;
879 if( nColor
!= -1 )//automatic font color does not work for lines -> fallback to black
880 aVLineProperties
.Color
= uno::makeAny(nColor
);
882 m_pShapeFactory
->createLine2D( rInfo
.xTextTarget
, aPoints
, &aVLineProperties
);
887 //.............................................................................
889 //.............................................................................