1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "PieChart.hxx"
21 #include "PlottingPositionHelper.hxx"
22 #include "ShapeFactory.hxx"
23 #include "PolarLabelPositionHelper.hxx"
25 #include "CommonConverters.hxx"
26 #include "ViewDefines.hxx"
27 #include "ObjectIdentifier.hxx"
29 #include <com/sun/star/chart/DataLabelPlacement.hpp>
30 #include <com/sun/star/chart2/XColorScheme.hpp>
32 #include <com/sun/star/container/XChild.hpp>
33 #include <rtl/math.hxx>
35 #include <boost/scoped_ptr.hpp>
37 //.............................................................................
40 //.............................................................................
41 using namespace ::com::sun::star
;
42 using namespace ::com::sun::star::chart2
;
44 class PiePositionHelper
: public PolarPlottingPositionHelper
47 PiePositionHelper( NormalAxis eNormalAxis
, double fAngleDegreeOffset
);
48 virtual ~PiePositionHelper();
50 bool getInnerAndOuterRadius( double fCategoryX
, double& fLogicInnerRadius
, double& fLogicOuterRadius
, bool bUseRings
, double fMaxOffset
) const;
53 //Distance between different category rings, seen relative to width of a ring:
54 double m_fRingDistance
; //>=0 m_fRingDistance=1 --> distance == width
57 PiePositionHelper::PiePositionHelper( NormalAxis eNormalAxis
, double fAngleDegreeOffset
)
58 : PolarPlottingPositionHelper(eNormalAxis
)
59 , m_fRingDistance(0.0)
61 m_fRadiusOffset
= 0.0;
62 m_fAngleDegreeOffset
= fAngleDegreeOffset
;
65 PiePositionHelper::~PiePositionHelper()
69 bool PiePositionHelper::getInnerAndOuterRadius( double fCategoryX
70 , double& fLogicInnerRadius
, double& fLogicOuterRadius
71 , bool bUseRings
, double fMaxOffset
) const
76 bool bIsVisible
= true;
77 double fLogicInner
= fCategoryX
-0.5+m_fRingDistance
/2.0;
78 double fLogicOuter
= fCategoryX
+0.5-m_fRingDistance
/2.0;
80 if( !isMathematicalOrientationRadius() )
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 fLogicInner
+= fMaxOffset
;
85 fLogicOuter
+= fMaxOffset
;
88 if( fLogicInner
>= getLogicMaxX() )
90 if( fLogicOuter
<= getLogicMinX() )
93 if( fLogicInner
< getLogicMinX() )
94 fLogicInner
= getLogicMinX();
95 if( fLogicOuter
> getLogicMaxX() )
96 fLogicOuter
= getLogicMaxX();
98 fLogicInnerRadius
= fLogicInner
;
99 fLogicOuterRadius
= fLogicOuter
;
100 if( !isMathematicalOrientationRadius() )
101 std::swap(fLogicInnerRadius
,fLogicOuterRadius
);
105 //-----------------------------------------------------------------------------
106 //-----------------------------------------------------------------------------
107 //-----------------------------------------------------------------------------
109 PieChart::PieChart( const uno::Reference
<XChartType
>& xChartTypeModel
110 , sal_Int32 nDimensionCount
111 , bool bExcludingPositioning
)
112 : VSeriesPlotter( xChartTypeModel
, nDimensionCount
)
113 , m_pPosHelper( new PiePositionHelper( NormalAxis_Z
, (m_nDimension
==3)?0.0:90.0 ) )
115 , m_bSizeExcludesLabelsAndExplodedSegments(bExcludingPositioning
)
117 ::rtl::math::setNan(&m_fMaxOffset
);
119 PlotterBase::m_pPosHelper
= m_pPosHelper
;
120 VSeriesPlotter::m_pMainPosHelper
= m_pPosHelper
;
121 m_pPosHelper
->m_fRadiusOffset
= 0.0;
122 m_pPosHelper
->m_fRingDistance
= 0.0;
124 uno::Reference
< beans::XPropertySet
> xChartTypeProps( xChartTypeModel
, uno::UNO_QUERY
);
125 if( xChartTypeProps
.is() ) try
127 xChartTypeProps
->getPropertyValue( "UseRings") >>= m_bUseRings
;
130 m_pPosHelper
->m_fRadiusOffset
= 1.0;
131 if( nDimensionCount
==3 )
132 m_pPosHelper
->m_fRingDistance
= 0.1;
135 catch( const uno::Exception
& e
)
137 ASSERT_EXCEPTION( e
);
141 PieChart::~PieChart()
146 //-----------------------------------------------------------------
148 void PieChart::setScales( const std::vector
< ExplicitScaleData
>& rScales
, bool /* bSwapXAndYAxis */ )
150 OSL_ENSURE(m_nDimension
<=static_cast<sal_Int32
>(rScales
.size()),"Dimension of Plotter does not fit two dimension of given scale sequence");
151 m_pPosHelper
->setScales( rScales
, true );
154 //-----------------------------------------------------------------
156 drawing::Direction3D
PieChart::getPreferredDiagramAspectRatio() const
158 if( m_nDimension
== 3 )
159 return drawing::Direction3D(1,1,0.10);
160 return drawing::Direction3D(1,1,1);
163 bool PieChart::keepAspectRatio() const
165 if( m_nDimension
== 3 )
170 bool PieChart::shouldSnapRectToUsedArea()
175 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
)
183 //---------------------------
184 //transform position:
185 drawing::Direction3D aOffset
;
186 if( !::rtl::math::approxEqual( fExplodePercentage
, 0.0 ) )
188 double fAngle
= fUnitCircleStartAngleDegree
+ fUnitCircleWidthAngleDegree
/2.0;
189 double fRadius
= (fUnitCircleOuterRadius
-fUnitCircleInnerRadius
)*fExplodePercentage
;
190 drawing::Position3D aOrigin
= m_pPosHelper
->transformUnitCircleToScene( 0, 0, fLogicZ
);
191 drawing::Position3D aNewOrigin
= m_pPosHelper
->transformUnitCircleToScene( fAngle
, fRadius
, fLogicZ
);
192 aOffset
= aNewOrigin
- aOrigin
;
195 //---------------------------
197 uno::Reference
< drawing::XShape
> xShape(0);
200 xShape
= m_pShapeFactory
->createPieSegment( xTarget
201 , fUnitCircleStartAngleDegree
, fUnitCircleWidthAngleDegree
202 , fUnitCircleInnerRadius
, fUnitCircleOuterRadius
203 , aOffset
, B3DHomMatrixToHomogenMatrix( m_pPosHelper
->getUnitCartesianToScene() )
208 xShape
= m_pShapeFactory
->createPieSegment2D( xTarget
209 , fUnitCircleStartAngleDegree
, fUnitCircleWidthAngleDegree
210 , fUnitCircleInnerRadius
, fUnitCircleOuterRadius
211 , aOffset
, B3DHomMatrixToHomogenMatrix( m_pPosHelper
->getUnitCartesianToScene() ) );
213 this->setMappedProperties( xShape
, xObjectProperties
, PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), pOverwritePropertiesMap
);
217 void PieChart::addSeries( VDataSeries
* pSeries
, sal_Int32
/* zSlot */, sal_Int32
/* xSlot */, sal_Int32
/* ySlot */ )
219 VSeriesPlotter::addSeries( pSeries
, 0, -1, 0 );
222 double PieChart::getMinimumX()
226 double PieChart::getMaxOffset()
228 if (!::rtl::math::isNan(m_fMaxOffset
))
229 // Value already cached. Use it.
233 if( m_aZSlots
.size()<=0 )
235 if( m_aZSlots
[0].size()<=0 )
238 const ::std::vector
< VDataSeries
* >& rSeriesList( m_aZSlots
[0][0].m_aSeriesVector
);
239 if( rSeriesList
.size()<=0 )
242 VDataSeries
* pSeries
= rSeriesList
[0];
243 uno::Reference
< beans::XPropertySet
> xSeriesProp( pSeries
->getPropertiesOfSeries() );
244 if( !xSeriesProp
.is() )
247 double fExplodePercentage
=0.0;
248 xSeriesProp
->getPropertyValue( "Offset") >>= fExplodePercentage
;
249 if(fExplodePercentage
>m_fMaxOffset
)
250 m_fMaxOffset
=fExplodePercentage
;
252 if(!m_bSizeExcludesLabelsAndExplodedSegments
)
254 uno::Sequence
< sal_Int32
> aAttributedDataPointIndexList
;
255 if( xSeriesProp
->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList
)
257 for(sal_Int32 nN
=aAttributedDataPointIndexList
.getLength();nN
--;)
259 uno::Reference
< beans::XPropertySet
> xPointProp( pSeries
->getPropertiesOfPoint(aAttributedDataPointIndexList
[nN
]) );
262 fExplodePercentage
=0.0;
263 xPointProp
->getPropertyValue( "Offset") >>= fExplodePercentage
;
264 if(fExplodePercentage
>m_fMaxOffset
)
265 m_fMaxOffset
=fExplodePercentage
;
272 double PieChart::getMaximumX()
274 double fMaxOffset
= getMaxOffset();
275 if( m_aZSlots
.size()>0 && m_bUseRings
)
276 return m_aZSlots
[0].size()+0.5+fMaxOffset
;
277 return 1.5+fMaxOffset
;
279 double PieChart::getMinimumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32
/* nAxisIndex */ )
284 double PieChart::getMaximumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32
/* nAxisIndex */ )
289 bool PieChart::isExpandBorderToIncrementRhythm( sal_Int32
/* nDimensionIndex */ )
294 bool PieChart::isExpandIfValuesCloseToBorder( sal_Int32
/* nDimensionIndex */ )
299 bool PieChart::isExpandWideValuesToZero( sal_Int32
/* nDimensionIndex */ )
304 bool PieChart::isExpandNarrowValuesTowardZero( sal_Int32
/* nDimensionIndex */ )
309 bool PieChart::isSeparateStackingForDifferentSigns( sal_Int32
/* nDimensionIndex */ )
314 void PieChart::createShapes()
316 if( m_aZSlots
.begin() == m_aZSlots
.end() ) //no series
319 OSL_ENSURE(m_pShapeFactory
&&m_xLogicTarget
.is()&&m_xFinalTarget
.is(),"PieChart is not proper initialized");
320 if(!(m_pShapeFactory
&&m_xLogicTarget
.is()&&m_xFinalTarget
.is()))
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 createGroupShape( m_xLogicTarget
,OUString() ));
328 uno::Reference
< drawing::XShapes
> xTextTarget(
329 m_pShapeFactory
->createGroup2D( m_xFinalTarget
,OUString() ));
330 //---------------------------------------------
331 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
333 //=============================================================================
334 ::std::vector
< VDataSeriesGroup
>::iterator aXSlotIter
= m_aZSlots
[0].begin();
335 const ::std::vector
< VDataSeriesGroup
>::const_iterator aXSlotEnd
= m_aZSlots
[0].end();
337 ::std::vector
< VDataSeriesGroup
>::size_type nExplodeableSlot
= 0;
338 if( m_pPosHelper
->isMathematicalOrientationRadius() && m_bUseRings
)
339 nExplodeableSlot
= m_aZSlots
[0].size()-1;
341 m_aLabelInfoList
.clear();
342 ::rtl::math::setNan(&m_fMaxOffset
);
343 sal_Int32 n3DRelativeHeight
= 100;
344 uno::Reference
< beans::XPropertySet
> xPropertySet( m_xChartTypeModel
, uno::UNO_QUERY
);
345 if ( (m_nDimension
==3) && xPropertySet
.is())
349 uno::Any aAny
= xPropertySet
->getPropertyValue( "3DRelativeHeight" );
350 aAny
>>= n3DRelativeHeight
;
352 catch (const uno::Exception
&) { }
355 //=============================================================================
356 for( double fSlotX
=0; aXSlotIter
!= aXSlotEnd
&& (m_bUseRings
||fSlotX
<0.5 ); ++aXSlotIter
, fSlotX
+=1.0 )
358 ::std::vector
< VDataSeries
* >* pSeriesList
= &(aXSlotIter
->m_aSeriesVector
);
359 if( pSeriesList
->size()<=0 )//there should be only one series in each x slot
361 VDataSeries
* pSeries
= (*pSeriesList
)[0];
365 m_pPosHelper
->m_fAngleDegreeOffset
= pSeries
->getStartingAngle();
367 double fLogicYSum
= 0.0;
368 //iterate through all points to get the sum
369 sal_Int32 nPointIndex
=0;
370 sal_Int32 nPointCount
=pSeries
->getTotalPointCount();
371 for( nPointIndex
= 0; nPointIndex
< nPointCount
; nPointIndex
++ )
373 double fY
= pSeries
->getYValue( nPointIndex
);
376 //@todo warn somehow that negative values are treated as positive
378 if( ::rtl::math::isNan(fY
) )
380 fLogicYSum
+= fabs(fY
);
384 double fLogicYForNextPoint
= 0.0;
385 //iterate through all points to create shapes
386 for( nPointIndex
= 0; nPointIndex
< nPointCount
; nPointIndex
++ )
388 double fLogicInnerRadius
, fLogicOuterRadius
;
389 double fOffset
= getMaxOffset();
390 bool bIsVisible
= m_pPosHelper
->getInnerAndOuterRadius( fSlotX
+1.0, fLogicInnerRadius
, fLogicOuterRadius
, m_bUseRings
, fOffset
);
394 double fDepth
= this->getTransformedDepth() * (n3DRelativeHeight
/ 100.0);
396 uno::Reference
< drawing::XShapes
> xSeriesGroupShape_Shapes
= getSeriesGroupShape(pSeries
, xSeriesTarget
);
397 //collect data point information (logic coordinates, style ):
398 double fLogicYValue
= fabs(pSeries
->getYValue( nPointIndex
));
399 if( ::rtl::math::isNan(fLogicYValue
) )
401 if(fLogicYValue
==0.0)//@todo: continue also if the resolution to small
403 double fLogicYPos
= fLogicYForNextPoint
;
404 fLogicYForNextPoint
+= fLogicYValue
;
406 uno::Reference
< beans::XPropertySet
> xPointProperties
= pSeries
->getPropertiesOfPoint( nPointIndex
);
408 //iterate through all subsystems to create partial points
410 //logic values on angle axis:
411 double fLogicStartAngleValue
= fLogicYPos
/fLogicYSum
;
412 double fLogicEndAngleValue
= (fLogicYPos
+fLogicYValue
)/fLogicYSum
;
414 double fExplodePercentage
=0.0;
415 bool bDoExplode
= ( nExplodeableSlot
== static_cast< ::std::vector
< VDataSeriesGroup
>::size_type
>(fSlotX
) );
418 xPointProperties
->getPropertyValue( "Offset") >>= fExplodePercentage
;
420 catch( const uno::Exception
& e
)
422 ASSERT_EXCEPTION( e
);
425 //---------------------------
426 //transforme to unit circle:
427 double fUnitCircleWidthAngleDegree
= m_pPosHelper
->getWidthAngleDegree( fLogicStartAngleValue
, fLogicEndAngleValue
);
428 double fUnitCircleStartAngleDegree
= m_pPosHelper
->transformToAngleDegree( fLogicStartAngleValue
);
429 double fUnitCircleInnerRadius
= m_pPosHelper
->transformToRadius( fLogicInnerRadius
);
430 double fUnitCircleOuterRadius
= m_pPosHelper
->transformToRadius( fLogicOuterRadius
);
432 //---------------------------
434 boost::scoped_ptr
< tPropertyNameValueMap
> apOverwritePropertiesMap(NULL
);
436 if(!pSeries
->hasPointOwnColor(nPointIndex
) && m_xColorScheme
.is())
438 apOverwritePropertiesMap
.reset( new tPropertyNameValueMap() );
439 (*apOverwritePropertiesMap
)["FillColor"] = uno::makeAny(
440 m_xColorScheme
->getColorByIndex( nPointIndex
));
445 double fLogicZ
= -1.0; // For 3D pie chart label position
446 uno::Reference
<drawing::XShape
> xPointShape(
447 createDataPoint( xSeriesGroupShape_Shapes
, xPointProperties
448 , fUnitCircleStartAngleDegree
, fUnitCircleWidthAngleDegree
449 , fUnitCircleInnerRadius
, fUnitCircleOuterRadius
450 , fLogicZ
, fDepth
, fExplodePercentage
, apOverwritePropertiesMap
.get() ) );
453 if( pSeries
->getDataPointLabelIfLabel(nPointIndex
) )
455 if( !::rtl::math::approxEqual( fExplodePercentage
, 0.0 ) )
457 double fExplodeOffset
= (fUnitCircleOuterRadius
-fUnitCircleInnerRadius
)*fExplodePercentage
;
458 fUnitCircleInnerRadius
+= fExplodeOffset
;
459 fUnitCircleOuterRadius
+= fExplodeOffset
;
462 sal_Int32 nLabelPlacement
= pSeries
->getLabelPlacement( nPointIndex
, m_xChartTypeModel
, m_nDimension
, m_pPosHelper
->isSwapXAndY() );
464 // AVOID_OVERLAP is in fact "Best fit" in the UI.
465 bool bMovementAllowed
= ( nLabelPlacement
== ::com::sun::star::chart::DataLabelPlacement::AVOID_OVERLAP
);
466 if( bMovementAllowed
)
467 // Use center for "Best fit" for now. In the future we
468 // may want to implement a real best fit algorithm.
469 // But center is good enough, and close to what Excel
471 nLabelPlacement
= ::com::sun::star::chart::DataLabelPlacement::CENTER
;
473 LabelAlignment
eAlignment(LABEL_ALIGN_CENTER
);
474 sal_Int32 nScreenValueOffsetInRadiusDirection
= 0 ;
475 if( nLabelPlacement
== ::com::sun::star::chart::DataLabelPlacement::OUTSIDE
)
476 nScreenValueOffsetInRadiusDirection
= (3!=m_nDimension
) ? 150 : 0;//todo maybe calculate this font height dependent
477 else if( nLabelPlacement
== ::com::sun::star::chart::DataLabelPlacement::INSIDE
)
478 nScreenValueOffsetInRadiusDirection
= (3!=m_nDimension
) ? -150 : 0;//todo maybe calculate this font height dependent
479 PolarLabelPositionHelper
aPolarPosHelper(m_pPosHelper
,m_nDimension
,m_xLogicTarget
,m_pShapeFactory
);
480 awt::Point
aScreenPosition2D(
481 aPolarPosHelper
.getLabelScreenPositionAndAlignmentForUnitCircleValues(eAlignment
, nLabelPlacement
482 , fUnitCircleStartAngleDegree
, fUnitCircleWidthAngleDegree
483 , fUnitCircleInnerRadius
, fUnitCircleOuterRadius
, fLogicZ
+0.5, 0 ));
485 PieLabelInfo aPieLabelInfo
;
486 aPieLabelInfo
.aFirstPosition
= basegfx::B2IVector( aScreenPosition2D
.X
, aScreenPosition2D
.Y
);
487 awt::Point
aOrigin( aPolarPosHelper
.transformSceneToScreenPosition( m_pPosHelper
->transformUnitCircleToScene( 0.0, 0.0, fLogicZ
+1.0 ) ) );
488 aPieLabelInfo
.aOrigin
= basegfx::B2IVector( aOrigin
.X
, aOrigin
.Y
);
490 //add a scaling independent Offset if requested
491 if( nScreenValueOffsetInRadiusDirection
!= 0)
493 basegfx::B2IVector
aDirection( aScreenPosition2D
.X
- aOrigin
.X
, aScreenPosition2D
.Y
- aOrigin
.Y
);
494 aDirection
.setLength(nScreenValueOffsetInRadiusDirection
);
495 aScreenPosition2D
.X
+= aDirection
.getX();
496 aScreenPosition2D
.Y
+= aDirection
.getY();
499 aPieLabelInfo
.xTextShape
= this->createDataLabel( xTextTarget
, *pSeries
, nPointIndex
500 , fLogicYValue
, fLogicYSum
, aScreenPosition2D
, eAlignment
);
502 uno::Reference
< container::XChild
> xChild( aPieLabelInfo
.xTextShape
, uno::UNO_QUERY
);
504 aPieLabelInfo
.xLabelGroupShape
= uno::Reference
<drawing::XShape
>( xChild
->getParent(), uno::UNO_QUERY
);
505 aPieLabelInfo
.fValue
= fLogicYValue
;
506 aPieLabelInfo
.bMovementAllowed
= bMovementAllowed
;
507 aPieLabelInfo
.bMoved
= false;
508 aPieLabelInfo
.xTextTarget
= xTextTarget
;
509 m_aLabelInfoList
.push_back(aPieLabelInfo
);
514 ShapeFactory::setShapeName( xPointShape
515 , ObjectIdentifier::createPointCID( pSeries
->getPointCID_Stub(), nPointIndex
) );
519 //enable dragging of outer segments
521 double fAngle
= fUnitCircleStartAngleDegree
+ fUnitCircleWidthAngleDegree
/2.0;
522 double fMaxDeltaRadius
= fUnitCircleOuterRadius
-fUnitCircleInnerRadius
;
523 drawing::Position3D aOrigin
= m_pPosHelper
->transformUnitCircleToScene( fAngle
, fUnitCircleOuterRadius
, fLogicZ
);
524 drawing::Position3D aNewOrigin
= m_pPosHelper
->transformUnitCircleToScene( fAngle
, fUnitCircleOuterRadius
+ fMaxDeltaRadius
, fLogicZ
);
526 sal_Int32
nOffsetPercent( static_cast<sal_Int32
>(fExplodePercentage
* 100.0) );
528 awt::Point
aMinimumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
529 aOrigin
, m_xLogicTarget
, m_pShapeFactory
, m_nDimension
) );
530 awt::Point
aMaximumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
531 aNewOrigin
, m_xLogicTarget
, m_pShapeFactory
, m_nDimension
) );
533 //enable draging of piesegments
534 OUString
aPointCIDStub( ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT
535 , pSeries
->getSeriesParticle()
536 , ObjectIdentifier::getPieSegmentDragMethodServiceName()
537 , ObjectIdentifier::createPieSegmentDragParameterString(
538 nOffsetPercent
, aMinimumPosition
, aMaximumPosition
)
541 ShapeFactory::setShapeName( xPointShape
542 , ObjectIdentifier::createPointCID( aPointCIDStub
, nPointIndex
) );
544 catch( const uno::Exception
& e
)
546 ASSERT_EXCEPTION( e
);
548 }//next series in x slot (next y slot)
551 //=============================================================================
552 //=============================================================================
553 //=============================================================================
554 /* @todo remove series shapes if empty
555 //remove and delete point-group-shape if empty
556 if(!xSeriesGroupShape_Shapes->getCount())
558 (*aSeriesIter)->m_xShape.set(NULL);
559 m_xLogicTarget->remove(xSeriesGroupShape_Shape);
563 //remove and delete series-group-shape if empty
571 ::basegfx::B2IRectangle
lcl_getRect( const uno::Reference
< drawing::XShape
>& xShape
)
573 ::basegfx::B2IRectangle aRect
;
575 aRect
= BaseGFXHelper::makeRectangle(xShape
->getPosition(),xShape
->getSize() );
579 bool lcl_isInsidePage( const awt::Point
& rPos
, const awt::Size
& rSize
, const awt::Size
& rPageSize
)
581 if( rPos
.X
< 0 || rPos
.Y
< 0 )
583 if( (rPos
.X
+ rSize
.Width
) > rPageSize
.Width
)
585 if( (rPos
.Y
+ rSize
.Height
) > rPageSize
.Height
)
590 }//end anonymous namespace
592 PieChart::PieLabelInfo::PieLabelInfo()
593 : xTextShape(0), xLabelGroupShape(0), aFirstPosition(), aOrigin(), fValue(0.0)
594 , bMovementAllowed(false), bMoved(false), xTextTarget(0), pPrevious(0),pNext(0)
598 bool PieChart::PieLabelInfo::moveAwayFrom( const PieChart::PieLabelInfo
* pFix
, const awt::Size
& rPageSize
, bool bMoveHalfWay
, bool bMoveClockwise
, bool bAlternativeMoveDirection
)
600 //return true if the move was successful
601 if(!this->bMovementAllowed
)
604 const sal_Int32 nLabelDistanceX
= rPageSize
.Width
/50;
605 const sal_Int32 nLabelDistanceY
= rPageSize
.Height
/50;
607 ::basegfx::B2IRectangle
aOverlap( lcl_getRect( this->xLabelGroupShape
) );
608 aOverlap
.intersect( lcl_getRect( pFix
->xLabelGroupShape
) );
609 if( !aOverlap
.isEmpty() )
611 (void)bAlternativeMoveDirection
;//todo
613 basegfx::B2IVector aRadiusDirection
= this->aFirstPosition
- this->aOrigin
;
614 aRadiusDirection
.setLength(1.0);
615 basegfx::B2IVector
aTangentialDirection( -aRadiusDirection
.getY(), aRadiusDirection
.getX() );
616 bool bShiftHorizontal
= abs(aTangentialDirection
.getX()) > abs(aTangentialDirection
.getY());
618 sal_Int32 nShift
= bShiftHorizontal
? static_cast<sal_Int32
>(aOverlap
.getWidth()) : static_cast<sal_Int32
>(aOverlap
.getHeight());
619 nShift
+= (bShiftHorizontal
? nLabelDistanceX
: nLabelDistanceY
);
624 awt::Point
aOldPos( this->xLabelGroupShape
->getPosition() );
625 basegfx::B2IVector aNewPos
= basegfx::B2IVector( aOldPos
.X
, aOldPos
.Y
) + nShift
*aTangentialDirection
;
627 //check whether the new position is ok
628 awt::Point
aNewAWTPos( aNewPos
.getX(), aNewPos
.getY() );
629 if( !lcl_isInsidePage( aNewAWTPos
, this->xLabelGroupShape
->getSize(), rPageSize
) )
632 this->xLabelGroupShape
->setPosition( aNewAWTPos
);
638 void PieChart::resetLabelPositionsToPreviousState()
640 std::vector
< PieLabelInfo
>::iterator aIt
= m_aLabelInfoList
.begin();
641 std::vector
< PieLabelInfo
>::const_iterator aEnd
= m_aLabelInfoList
.end();
642 for( ;aIt
!=aEnd
; ++aIt
)
643 aIt
->xLabelGroupShape
->setPosition(aIt
->aPreviousPosition
);
646 bool PieChart::detectLabelOverlapsAndMove( const awt::Size
& rPageSize
)
648 //returns true when there might be more to do
650 //find borders of a group of overlapping labels
651 bool bOverlapFound
= false;
652 PieLabelInfo
* pStart
= &(*(m_aLabelInfoList
.rbegin()));
653 PieLabelInfo
* pFirstBorder
= 0;
654 PieLabelInfo
* pSecondBorder
= 0;
655 PieLabelInfo
* pCurrent
= pStart
;
658 ::basegfx::B2IRectangle
aPreviousOverlap( lcl_getRect( pCurrent
->xLabelGroupShape
) );
659 ::basegfx::B2IRectangle
aNextOverlap( aPreviousOverlap
);
660 aPreviousOverlap
.intersect( lcl_getRect( pCurrent
->pPrevious
->xLabelGroupShape
) );
661 aNextOverlap
.intersect( lcl_getRect( pCurrent
->pNext
->xLabelGroupShape
) );
663 bool bPreviousOverlap
= !aPreviousOverlap
.isEmpty();
664 bool bNextOverlap
= !aNextOverlap
.isEmpty();
665 if( bPreviousOverlap
|| bNextOverlap
)
666 bOverlapFound
= true;
667 if( !bPreviousOverlap
&& bNextOverlap
)
669 pFirstBorder
= pCurrent
;
672 pCurrent
= pCurrent
->pNext
;
674 while( pCurrent
!= pStart
);
681 pCurrent
= pFirstBorder
;
684 ::basegfx::B2IRectangle
aPreviousOverlap( lcl_getRect( pCurrent
->xLabelGroupShape
) );
685 ::basegfx::B2IRectangle
aNextOverlap( aPreviousOverlap
);
686 aPreviousOverlap
.intersect( lcl_getRect( pCurrent
->pPrevious
->xLabelGroupShape
) );
687 aNextOverlap
.intersect( lcl_getRect( pCurrent
->pNext
->xLabelGroupShape
) );
689 if( !aPreviousOverlap
.isEmpty() && aNextOverlap
.isEmpty() )
691 pSecondBorder
= pCurrent
;
694 pCurrent
= pCurrent
->pNext
;
696 while( pCurrent
!= pFirstBorder
);
699 if( !pFirstBorder
|| !pSecondBorder
)
701 pFirstBorder
= &(*(m_aLabelInfoList
.rbegin()));
702 pSecondBorder
= &(*(m_aLabelInfoList
.begin()));
706 PieLabelInfo
* pCenter
= pFirstBorder
;
707 sal_Int32 nOverlapGroupCount
= 1;
708 for( pCurrent
= pFirstBorder
;pCurrent
!= pSecondBorder
; pCurrent
= pCurrent
->pNext
)
709 nOverlapGroupCount
++;
710 sal_Int32 nCenterPos
= nOverlapGroupCount
/2;
711 bool bSingleCenter
= nOverlapGroupCount
%2 != 0;
716 pCurrent
= pFirstBorder
;
717 while( --nCenterPos
)
718 pCurrent
= pCurrent
->pNext
;
722 //remind current positions
726 pCurrent
->aPreviousPosition
= pCurrent
->xLabelGroupShape
->getPosition();
727 pCurrent
= pCurrent
->pNext
;
729 while( pCurrent
!= pStart
);
732 bool bAlternativeMoveDirection
= false;
733 if( !tryMoveLabels( pFirstBorder
, pSecondBorder
, pCenter
, bSingleCenter
, bAlternativeMoveDirection
, rPageSize
) )
734 tryMoveLabels( pFirstBorder
, pSecondBorder
, pCenter
, bSingleCenter
, bAlternativeMoveDirection
, rPageSize
);
738 bool PieChart::tryMoveLabels( PieLabelInfo
* pFirstBorder
, PieLabelInfo
* pSecondBorder
739 , PieLabelInfo
* pCenter
740 , bool bSingleCenter
, bool& rbAlternativeMoveDirection
, const awt::Size
& rPageSize
)
742 PieLabelInfo
* p1
= bSingleCenter
? pCenter
->pPrevious
: pCenter
;
743 PieLabelInfo
* p2
= pCenter
->pNext
;
744 //return true when successful
746 bool bLabelOrderIsAntiClockWise
= m_pPosHelper
->isMathematicalOrientationAngle();
748 PieLabelInfo
* pCurrent
= 0;
749 for( pCurrent
= p2
;pCurrent
->pPrevious
!= pSecondBorder
; pCurrent
= pCurrent
->pNext
)
751 PieLabelInfo
* pFix
= 0;
752 for( pFix
= p2
->pPrevious
;pFix
!= pCurrent
; pFix
= pFix
->pNext
)
754 if( !pCurrent
->moveAwayFrom( pFix
, rPageSize
, !bSingleCenter
&& pCurrent
== p2
, !bLabelOrderIsAntiClockWise
, rbAlternativeMoveDirection
) )
756 if( !rbAlternativeMoveDirection
)
758 rbAlternativeMoveDirection
= true;
759 resetLabelPositionsToPreviousState();
765 for( pCurrent
= p1
;pCurrent
->pNext
!= pFirstBorder
; pCurrent
= pCurrent
->pPrevious
)
767 PieLabelInfo
* pFix
= 0;
768 for( pFix
= p2
->pNext
;pFix
!= pCurrent
; pFix
= pFix
->pPrevious
)
770 if( !pCurrent
->moveAwayFrom( pFix
, rPageSize
, false, bLabelOrderIsAntiClockWise
, rbAlternativeMoveDirection
) )
772 if( !rbAlternativeMoveDirection
)
774 rbAlternativeMoveDirection
= true;
775 resetLabelPositionsToPreviousState();
784 void PieChart::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size
& rPageSize
)
786 //------------------------------------------------------------------
787 //check whether there are any labels that should be moved
788 std::vector
< PieLabelInfo
>::iterator aIt1
= m_aLabelInfoList
.begin();
789 std::vector
< PieLabelInfo
>::const_iterator aEnd
= m_aLabelInfoList
.end();
790 bool bMoveableFound
= false;
791 for( ;aIt1
!=aEnd
; ++aIt1
)
793 if(aIt1
->bMovementAllowed
)
795 bMoveableFound
= true;
802 double fPageDiagonaleLength
= sqrt( double( rPageSize
.Width
*rPageSize
.Width
+ rPageSize
.Height
*rPageSize
.Height
) );
803 if( ::rtl::math::approxEqual( fPageDiagonaleLength
, 0.0 ) )
806 //------------------------------------------------------------------
807 //init next and previous
808 aIt1
= m_aLabelInfoList
.begin();
809 std::vector
< PieLabelInfo
>::iterator aIt2
= aIt1
;
810 if( aIt1
==aEnd
)//no need to do anything when we only have one label
812 aIt1
->pPrevious
= &(*(m_aLabelInfoList
.rbegin()));
814 for( ;aIt2
!=aEnd
; ++aIt1
, ++aIt2
)
816 PieLabelInfo
& rInfo1( *aIt1
);
817 PieLabelInfo
& rInfo2( *aIt2
);
818 rInfo1
.pNext
= &rInfo2
;
819 rInfo2
.pPrevious
= &rInfo1
;
821 aIt1
->pNext
= &(*(m_aLabelInfoList
.begin()));
824 //------------------------------------------------------------------
825 //detect overlaps and move
826 sal_Int32 nMaxIterations
= 50;
827 while( detectLabelOverlapsAndMove( rPageSize
) && nMaxIterations
> 0 )
830 //------------------------------------------------------------------
831 //create connection lines for the moved labels
832 aEnd
= m_aLabelInfoList
.end();
833 VLineProperties aVLineProperties
;
834 for( aIt1
= m_aLabelInfoList
.begin(); aIt1
!=aEnd
; ++aIt1
)
836 PieLabelInfo
& rInfo( *aIt1
);
839 sal_Int32 nX1
= rInfo
.aFirstPosition
.getX();
840 sal_Int32 nY1
= rInfo
.aFirstPosition
.getY();
843 ::basegfx::B2IRectangle
aRect( lcl_getRect( rInfo
.xLabelGroupShape
) );
844 if( nX1
< aRect
.getMinX() )
845 nX2
= aRect
.getMinX();
846 else if( nX1
> aRect
.getMaxX() )
847 nX2
= aRect
.getMaxX();
849 if( nY1
< aRect
.getMinY() )
850 nY2
= aRect
.getMinY();
851 else if( nY1
> aRect
.getMaxY() )
852 nY2
= aRect
.getMaxY();
855 //when the line is very short compared to the page size don't create one
856 ::basegfx::B2DVector
aLength(nX1
-nX2
, nY1
-nY2
);
857 if( (aLength
.getLength()/fPageDiagonaleLength
) < 0.01 )
860 drawing::PointSequenceSequence
aPoints(1);
861 aPoints
[0].realloc(2);
862 aPoints
[0][0].X
= nX1
;
863 aPoints
[0][0].Y
= nY1
;
864 aPoints
[0][1].X
= nX2
;
865 aPoints
[0][1].Y
= nY2
;
867 uno::Reference
< beans::XPropertySet
> xProp( rInfo
.xTextShape
, uno::UNO_QUERY
);
870 sal_Int32 nColor
= 0;
871 xProp
->getPropertyValue("CharColor") >>= nColor
;
872 if( nColor
!= -1 )//automatic font color does not work for lines -> fallback to black
873 aVLineProperties
.Color
= uno::makeAny(nColor
);
875 m_pShapeFactory
->createLine2D( rInfo
.xTextTarget
, aPoints
, &aVLineProperties
);
880 //.............................................................................
882 //.............................................................................
884 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */