merge the formfield patch from ooo-build
[ooovba.git] / chart2 / source / view / charttypes / PieChart.cxx
blob3596d1758d9d1d90b98ae3719113dac164633116
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
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"
39 #include "macros.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 //.............................................................................
55 namespace chart
57 //.............................................................................
58 using namespace ::com::sun::star;
59 using namespace ::com::sun::star::chart2;
61 class PiePositionHelper : public PolarPlottingPositionHelper
63 public:
64 PiePositionHelper( NormalAxis eNormalAxis, double fAngleDegreeOffset );
65 virtual ~PiePositionHelper();
67 bool getInnerAndOuterRadius( double fCategoryX, double& fLogicInnerRadius, double& fLogicOuterRadius, bool bUseRings, double fMaxOffset ) const;
69 public:
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
90 if( !bUseRings )
91 fCategoryX = 1.0;
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() )
106 return false;
107 if( fLogicOuter <= getLogicMinX() )
108 return false;
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);
119 return bIsVisible;
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 ) )
130 , m_bUseRings(false)
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;
143 if( 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()
158 delete m_pPosHelper;
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 )
183 return false;
184 return true;
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;
198 return aSNS;
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 //---------------------------
223 //create point
224 uno::Reference< drawing::XShape > xShape(0);
225 if(m_nDimension==3)
227 xShape = m_pShapeFactory->createPieSegment( xTarget
228 , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree
229 , fUnitCircleInnerRadius, fUnitCircleOuterRadius
230 , aOffset, B3DHomMatrixToHomogenMatrix( m_pPosHelper->getUnitCartesianToScene() )
231 , fDepth );
233 else
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 );
241 return xShape;
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()
251 return 0.5;
253 double PieChart::getMaxOffset()
255 if (!::rtl::math::isNan(m_fMaxOffset))
256 // Value already cached. Use it.
257 return m_fMaxOffset;
259 m_fMaxOffset = 0.0;
260 if( m_aZSlots.size()<=0 )
261 return m_fMaxOffset;
262 if( m_aZSlots[0].size()<=0 )
263 return m_fMaxOffset;
265 const ::std::vector< VDataSeries* >& rSeriesList( m_aZSlots[0][0].m_aSeriesVector );
266 if( rSeriesList.size()<=0 )
267 return m_fMaxOffset;
269 VDataSeries* pSeries = rSeriesList[0];
270 uno::Reference< beans::XPropertySet > xSeriesProp( pSeries->getPropertiesOfSeries() );
271 if( !xSeriesProp.is() )
272 return m_fMaxOffset;
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]) );
285 if(xPointProp.is())
287 fExplodePercentage=0.0;
288 xPointProp->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage;
289 if(fExplodePercentage>m_fMaxOffset)
290 m_fMaxOffset=fExplodePercentage;
294 return m_fMaxOffset;
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 */ )
305 return 0.0;
308 double PieChart::getMaximumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32 /* nAxisIndex */ )
310 return 1.0;
313 bool PieChart::isExpandBorderToIncrementRhythm( sal_Int32 /* nDimensionIndex */ )
315 return false;
318 bool PieChart::isExpandIfValuesCloseToBorder( sal_Int32 /* nDimensionIndex */ )
320 return false;
323 bool PieChart::isExpandWideValuesToZero( sal_Int32 /* nDimensionIndex */ )
325 return false;
328 bool PieChart::isExpandNarrowValuesTowardZero( sal_Int32 /* nDimensionIndex */ )
330 return false;
333 bool PieChart::isSeperateStackingForDifferentSigns( sal_Int32 /* nDimensionIndex */ )
335 return false;
338 void PieChart::createShapes()
340 if( m_aZSlots.begin() == m_aZSlots.end() ) //no series
341 return;
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()))
345 return;
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
373 continue;
374 VDataSeries* pSeries = (*pSeriesList)[0];
375 if(!pSeries)
376 continue;
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 );
387 if(fY<0.0)
389 //@todo warn somehow that negative values are treated as positive
391 if( ::rtl::math::isNan(fY) )
392 continue;
393 fLogicYSum += fabs(fY);
395 if(fLogicYSum==0.0)
396 continue;
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() );
403 if( !bIsVisible )
404 continue;
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) )
414 continue;
415 if(fLogicYValue==0.0)//@todo: continue also if the resolution to small
416 continue;
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) );
430 if(bDoExplode) try
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 //---------------------------
447 //point color:
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 ));
458 //create data point
459 uno::Reference<drawing::XShape> xPointShape(
460 createDataPoint( xSeriesGroupShape_Shapes, xPointProperties
461 , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree
462 , fUnitCircleInnerRadius, fUnitCircleOuterRadius
463 , fLogicZ, fDepth, fExplodePercentage, apOverwritePropertiesMap.get() ) );
465 //create label
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 );
510 if( xChild.is() )
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);
519 if(!bDoExplode)
521 ShapeFactory::setShapeName( xPointShape
522 , ObjectIdentifier::createPointCID( pSeries->getPointCID_Stub(), nPointIndex ) );
524 else try
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 )
546 ) );
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)
556 }//next category
557 }//next x 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
572 //... todo
575 namespace
578 ::basegfx::B2IRectangle lcl_getRect( const uno::Reference< drawing::XShape >& xShape )
580 ::basegfx::B2IRectangle aRect;
581 if( xShape.is() )
582 aRect = BaseGFXHelper::makeRectangle(xShape->getPosition(),xShape->getSize() );
583 return aRect;
586 bool lcl_isInsidePage( const awt::Point& rPos, const awt::Size& rSize, const awt::Size& rPageSize )
588 if( rPos.X < 0 || rPos.Y < 0 )
589 return false;
590 if( (rPos.X + rSize.Width) > rPageSize.Width )
591 return false;
592 if( (rPos.Y + rSize.Height) > rPageSize.Height )
593 return false;
594 return true;
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)
609 return false;
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);
627 if( bMoveHalfWay )
628 nShift/=2;
629 if(!bMoveClockwise)
630 nShift*=-1;
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 ) )
637 return false;
639 this->xLabelGroupShape->setPosition( aNewAWTPos );
640 this->bMoved = true;
642 return true;
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;
677 break;
679 pCurrent = pCurrent->pNext;
681 while( pCurrent != pStart );
683 if( !bOverlapFound )
684 return false;
686 if( pFirstBorder )
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;
699 break;
701 pCurrent = pCurrent->pNext;
703 while( pCurrent != pFirstBorder );
706 if( !pFirstBorder || !pSecondBorder )
708 pFirstBorder = &(*(m_aLabelInfoList.rbegin()));
709 pSecondBorder = &(*(m_aLabelInfoList.begin()));
712 //find center
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;
719 if( bSingleCenter )
720 nCenterPos++;
721 if(nCenterPos>1)
723 pCurrent = pFirstBorder;
724 while( --nCenterPos )
725 pCurrent = pCurrent->pNext;
726 pCenter = pCurrent;
729 //remind current positions
730 pCurrent = pStart;
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 );
742 return true;
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();
767 return false;
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();
783 return false;
788 return true;
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;
803 break;
806 if(!bMoveableFound)
807 return;
809 double fPageDiagonaleLength = sqrt( double( rPageSize.Width*rPageSize.Width + rPageSize.Height*rPageSize.Height) );
810 if( ::rtl::math::approxEqual( fPageDiagonaleLength, 0.0 ) )
811 return;
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
818 return;
819 aIt1->pPrevious = &(*(m_aLabelInfoList.rbegin()));
820 ++aIt2;
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 )
835 nMaxIterations--;
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 );
844 if( rInfo.bMoved )
846 sal_Int32 nX1 = rInfo.aFirstPosition.getX();
847 sal_Int32 nY1 = rInfo.aFirstPosition.getY();
848 sal_Int32 nX2 = nX1;
849 sal_Int32 nY2 = nY1;
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 )
865 continue;
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);
875 if( xProp.is() )
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 //.............................................................................
888 } //namespace chart
889 //.............................................................................