fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / chart2 / source / view / charttypes / VSeriesPlotter.cxx
blob52e75964b1ff274bdbc491997edd38d177efac77
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 "VSeriesPlotter.hxx"
21 #include "AbstractShapeFactory.hxx"
22 #include "chartview/ExplicitValueProvider.hxx"
24 #include "CommonConverters.hxx"
25 #include "macros.hxx"
26 #include "ViewDefines.hxx"
27 #include "ObjectIdentifier.hxx"
28 #include "StatisticsHelper.hxx"
29 #include "PlottingPositionHelper.hxx"
30 #include "LabelPositionHelper.hxx"
31 #include "ChartTypeHelper.hxx"
32 #include "Clipping.hxx"
33 #include "servicenames_charttypes.hxx"
34 #include "NumberFormatterWrapper.hxx"
35 #include "ContainerHelper.hxx"
36 #include "DataSeriesHelper.hxx"
37 #include "RegressionCurveHelper.hxx"
38 #include "VLegendSymbolFactory.hxx"
39 #include "FormattedStringHelper.hxx"
40 #include "ResId.hxx"
41 #include "Strings.hrc"
42 #include "RelativePositionHelper.hxx"
43 #include "DateHelper.hxx"
44 #include "DiagramHelper.hxx"
45 #include "defines.hxx"
47 //only for creation: @todo remove if all plotter are uno components and instantiated via servicefactory
48 #include "BarChart.hxx"
49 #include "PieChart.hxx"
50 #include "AreaChart.hxx"
51 #include "CandleStickChart.hxx"
52 #include "BubbleChart.hxx"
53 #include "NetChart.hxx"
54 #include <unonames.hxx>
56 #include <com/sun/star/chart/ErrorBarStyle.hpp>
57 #include <com/sun/star/chart/TimeUnit.hpp>
58 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
59 #include <com/sun/star/container/XChild.hpp>
60 #include <com/sun/star/chart2/RelativePosition.hpp>
61 #include <editeng/unoprnms.hxx>
62 #include <tools/color.hxx>
63 #include <rtl/ustrbuf.hxx>
64 #include <rtl/math.hxx>
65 #include <basegfx/vector/b2dvector.hxx>
66 #include <com/sun/star/drawing/LineStyle.hpp>
67 #include <com/sun/star/util/XCloneable.hpp>
69 #include <svx/unoshape.hxx>
71 #include <functional>
72 #include <map>
73 #include <unordered_map>
75 #include <boost/ptr_container/ptr_map.hpp>
77 namespace chart {
79 using namespace ::com::sun::star;
80 using namespace ::com::sun::star::chart2;
81 using ::com::sun::star::uno::Reference;
82 using ::com::sun::star::uno::Sequence;
84 VDataSeriesGroup::CachedYValues::CachedYValues()
85 : m_bValuesDirty(true)
86 , m_fMinimumY(0.0)
87 , m_fMaximumY(0.0)
91 VDataSeriesGroup::VDataSeriesGroup()
92 : m_aSeriesVector()
93 , m_bMaxPointCountDirty(true)
94 , m_nMaxPointCount(0)
95 , m_aListOfCachedYValues()
99 VDataSeriesGroup::VDataSeriesGroup( VDataSeries* pSeries )
100 : m_aSeriesVector(1,pSeries)
101 , m_bMaxPointCountDirty(true)
102 , m_nMaxPointCount(0)
103 , m_aListOfCachedYValues()
107 VDataSeriesGroup::~VDataSeriesGroup()
111 void VDataSeriesGroup::deleteSeries()
113 //delete all data series help objects:
114 ::std::vector< VDataSeries* >::const_iterator aIter = m_aSeriesVector.begin();
115 const ::std::vector< VDataSeries* >::const_iterator aEnd = m_aSeriesVector.end();
116 for( ; aIter != aEnd; ++aIter )
118 delete *aIter;
120 m_aSeriesVector.clear();
123 void VDataSeriesGroup::addSeries( VDataSeries* pSeries )
125 m_aSeriesVector.push_back(pSeries);
126 m_bMaxPointCountDirty=true;
129 sal_Int32 VDataSeriesGroup::getSeriesCount() const
131 return m_aSeriesVector.size();
134 VSeriesPlotter::VSeriesPlotter( const uno::Reference<XChartType>& xChartTypeModel
135 , sal_Int32 nDimensionCount, bool bCategoryXAxis )
136 : PlotterBase( nDimensionCount )
137 , m_pMainPosHelper( 0 )
138 , m_xChartTypeModel(xChartTypeModel)
139 , m_xChartTypeModelProps( uno::Reference< beans::XPropertySet >::query( xChartTypeModel ))
140 , m_aZSlots()
141 , m_bCategoryXAxis(bCategoryXAxis)
142 , m_nTimeResolution(::com::sun::star::chart::TimeUnit::DAY)
143 , m_aNullDate(30,12,1899)
144 , m_xColorScheme()
145 , m_pExplicitCategoriesProvider(0)
146 , m_bPointsWereSkipped(false)
148 SAL_WARN_IF(!m_xChartTypeModel.is(),"chart2","no XChartType available in view, fallback to default values may be wrong");
151 VSeriesPlotter::~VSeriesPlotter()
153 //delete all data series help objects:
154 ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin();
155 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
156 for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
158 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
159 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
160 for( ; aXSlotIter != aXSlotEnd; ++aXSlotIter )
162 aXSlotIter->deleteSeries();
164 aZSlotIter->clear();
166 m_aZSlots.clear();
168 tSecondaryPosHelperMap::iterator aPosIt = m_aSecondaryPosHelperMap.begin();
169 while( aPosIt != m_aSecondaryPosHelperMap.end() )
171 PlottingPositionHelper* pPosHelper = aPosIt->second;
172 delete pPosHelper;
174 ++aPosIt;
176 m_aSecondaryPosHelperMap.clear();
178 m_aSecondaryValueScales.clear();
181 void VSeriesPlotter::addSeries( VDataSeries* pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot )
183 //take ownership of pSeries
185 OSL_PRECOND( pSeries, "series to add is NULL" );
186 if(!pSeries)
187 return;
189 if(m_bCategoryXAxis)
191 if( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() )
192 pSeries->setXValues( m_pExplicitCategoriesProvider->getOriginalCategories() );
193 else
194 pSeries->setCategoryXAxis();
196 else
198 if( m_pExplicitCategoriesProvider )
199 pSeries->setXValuesIfNone( m_pExplicitCategoriesProvider->getOriginalCategories() );
202 if(zSlot<0 || zSlot>=static_cast<sal_Int32>(m_aZSlots.size()))
204 //new z slot
205 ::std::vector< VDataSeriesGroup > aZSlot;
206 aZSlot.push_back( VDataSeriesGroup(pSeries) );
207 m_aZSlots.push_back( aZSlot );
209 else
211 //existing zslot
212 ::std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[zSlot];
214 if(xSlot<0 || xSlot>=static_cast<sal_Int32>(rXSlots.size()))
216 //append the series to already existing x series
217 rXSlots.push_back( VDataSeriesGroup(pSeries) );
219 else
221 //x slot is already occupied
222 //y slot decides what to do:
224 VDataSeriesGroup& rYSlots = rXSlots[xSlot];
225 sal_Int32 nYSlotCount = rYSlots.getSeriesCount();
227 if( ySlot < -1 )
229 //move all existing series in the xSlot to next slot
230 //@todo
231 OSL_FAIL( "Not implemented yet");
233 else if( ySlot == -1 || ySlot >= nYSlotCount)
235 //append the series to already existing y series
236 rYSlots.addSeries(pSeries);
238 else
240 //y slot is already occupied
241 //insert at given y and x position
243 //@todo
244 OSL_FAIL( "Not implemented yet");
250 drawing::Direction3D VSeriesPlotter::getPreferredDiagramAspectRatio() const
252 drawing::Direction3D aRet(1.0,1.0,1.0);
253 if (!m_pPosHelper)
254 return aRet;
256 drawing::Direction3D aScale( m_pPosHelper->getScaledLogicWidth() );
257 aRet.DirectionZ = aScale.DirectionZ*0.2;
258 if(aRet.DirectionZ>1.0)
259 aRet.DirectionZ=1.0;
260 if(aRet.DirectionZ>10)
261 aRet.DirectionZ=10;
262 return aRet;
265 bool VSeriesPlotter::keepAspectRatio() const
267 return true;
270 void VSeriesPlotter::releaseShapes()
272 ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin();
273 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
274 for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
276 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
277 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
278 for( ; aXSlotIter != aXSlotEnd; ++aXSlotIter )
280 ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
282 ::std::vector< VDataSeries* >::iterator aSeriesIter = pSeriesList->begin();
283 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end();
285 //iterate through all series in this x slot
286 for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter )
288 VDataSeries* pSeries( *aSeriesIter );
289 pSeries->releaseShapes();
295 uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShape( VDataSeries* pDataSeries
296 , const uno::Reference< drawing::XShapes >& xTarget )
298 uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xGroupShape );
299 if( !xShapes.is() )
301 //create a group shape for this series and add to logic target:
302 xShapes = createGroupShape( xTarget,pDataSeries->getCID() );
303 pDataSeries->m_xGroupShape = xShapes;
305 return xShapes;
308 uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShapeFrontChild( VDataSeries* pDataSeries
309 , const uno::Reference< drawing::XShapes >& xTarget )
311 uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xFrontSubGroupShape );
312 if(!xShapes.is())
314 //ensure that the series group shape is already created
315 uno::Reference< drawing::XShapes > xSeriesShapes( this->getSeriesGroupShape( pDataSeries, xTarget ) );
316 //ensure that the back child is created first
317 this->getSeriesGroupShapeBackChild( pDataSeries, xTarget );
318 //use series group shape as parent for the new created front group shape
319 xShapes = createGroupShape( xSeriesShapes );
320 pDataSeries->m_xFrontSubGroupShape = xShapes;
322 return xShapes;
325 uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShapeBackChild( VDataSeries* pDataSeries
326 , const uno::Reference< drawing::XShapes >& xTarget )
328 uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xBackSubGroupShape );
329 if(!xShapes.is())
331 //ensure that the series group shape is already created
332 uno::Reference< drawing::XShapes > xSeriesShapes( this->getSeriesGroupShape( pDataSeries, xTarget ) );
333 //use series group shape as parent for the new created back group shape
334 xShapes = createGroupShape( xSeriesShapes );
335 pDataSeries->m_xBackSubGroupShape = xShapes;
337 return xShapes;
340 uno::Reference< drawing::XShapes > VSeriesPlotter::getLabelsGroupShape( VDataSeries& rDataSeries
341 , const uno::Reference< drawing::XShapes >& xTextTarget )
343 //xTextTarget needs to be a 2D shape container always!
345 uno::Reference< drawing::XShapes > xShapes( rDataSeries.m_xLabelsGroupShape );
346 if(!xShapes.is())
348 //create a 2D group shape for texts of this series and add to text target:
349 xShapes = m_pShapeFactory->createGroup2D( xTextTarget, rDataSeries.getLabelsCID() );
350 rDataSeries.m_xLabelsGroupShape = xShapes;
352 return xShapes;
355 uno::Reference< drawing::XShapes > VSeriesPlotter::getErrorBarsGroupShape( VDataSeries& rDataSeries
356 , const uno::Reference< drawing::XShapes >& xTarget
357 , bool bYError )
359 uno::Reference< ::com::sun::star::drawing::XShapes > &rShapeGroup =
360 bYError ? rDataSeries.m_xErrorYBarsGroupShape : rDataSeries.m_xErrorXBarsGroupShape;
362 uno::Reference< drawing::XShapes > xShapes( rShapeGroup );
363 if(!xShapes.is())
365 //create a group shape for this series and add to logic target:
366 xShapes = this->createGroupShape( xTarget,rDataSeries.getErrorBarsCID(bYError) );
367 rShapeGroup = xShapes;
369 return xShapes;
373 OUString VSeriesPlotter::getLabelTextForValue( VDataSeries& rDataSeries
374 , sal_Int32 nPointIndex
375 , double fValue
376 , bool bAsPercentage )
378 OUString aNumber;
380 if( m_apNumberFormatterWrapper.get())
382 sal_Int32 nNumberFormatKey = 0;
383 if( rDataSeries.hasExplicitNumberFormat(nPointIndex,bAsPercentage) )
384 nNumberFormatKey = rDataSeries.getExplicitNumberFormat(nPointIndex,bAsPercentage);
385 else if( bAsPercentage )
387 sal_Int32 nPercentFormat = DiagramHelper::getPercentNumberFormat( m_apNumberFormatterWrapper->getNumberFormatsSupplier() );
388 if( nPercentFormat != -1 )
389 nNumberFormatKey = nPercentFormat;
391 else
393 if( rDataSeries.shouldLabelNumberFormatKeyBeDetectedFromYAxis() && m_aAxesNumberFormats.hasFormat(1,rDataSeries.getAttachedAxisIndex()) ) //y-axis
394 nNumberFormatKey = m_aAxesNumberFormats.getFormat(1,rDataSeries.getAttachedAxisIndex());
395 else
396 nNumberFormatKey = rDataSeries.detectNumberFormatKey( nPointIndex );
398 if(nNumberFormatKey<0)
399 nNumberFormatKey=0;
401 sal_Int32 nLabelCol = 0;
402 bool bColChanged;
403 aNumber = m_apNumberFormatterWrapper->getFormattedString(
404 nNumberFormatKey, fValue, nLabelCol, bColChanged );
405 //@todo: change color of label if bColChanged is true
407 else
409 sal_Unicode cDecSeparator = '.';//@todo get this locale dependent
410 aNumber = ::rtl::math::doubleToUString( fValue, rtl_math_StringFormat_G /*rtl_math_StringFormat*/
411 , 3/*DecPlaces*/ , cDecSeparator, false /*bEraseTrailingDecZeros*/ );
413 return aNumber;
416 uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Reference< drawing::XShapes >& xTarget
417 , VDataSeries& rDataSeries
418 , sal_Int32 nPointIndex
419 , double fValue
420 , double fSumValue
421 , const awt::Point& rScreenPosition2D
422 , LabelAlignment eAlignment
423 , sal_Int32 nOffset )
425 uno::Reference< drawing::XShape > xTextShape;
429 awt::Point aScreenPosition2D(rScreenPosition2D);
430 if(LABEL_ALIGN_LEFT==eAlignment)
431 aScreenPosition2D.X -= nOffset;
432 else if(LABEL_ALIGN_RIGHT==eAlignment)
433 aScreenPosition2D.X += nOffset;
434 else if(LABEL_ALIGN_TOP==eAlignment)
435 aScreenPosition2D.Y -= nOffset;
436 else if(LABEL_ALIGN_BOTTOM==eAlignment)
437 aScreenPosition2D.Y += nOffset;
439 uno::Reference< drawing::XShapes > xTarget_ =
440 m_pShapeFactory->createGroup2D(
441 getLabelsGroupShape(rDataSeries, xTarget),
442 ObjectIdentifier::createPointCID( rDataSeries.getLabelCID_Stub(), nPointIndex));
444 //check whether the label needs to be created and how:
445 DataPointLabel* pLabel = rDataSeries.getDataPointLabelIfLabel( nPointIndex );
447 if( !pLabel )
448 return xTextShape;
450 //prepare legend symbol
452 // get the font size for the label through the "CharHeight" property
453 // attached to the passed data series entry.
454 // (By tracing font height values it results that for pie chart the
455 // font size is not the same for all labels, but here no font size
456 // modification occurs).
457 float fViewFontSize( 10.0 );
459 uno::Reference< beans::XPropertySet > xProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) );
460 if( xProps.is() )
461 xProps->getPropertyValue( "CharHeight") >>= fViewFontSize;
462 // pt -> 1/100th mm
463 fViewFontSize *= (2540.0f / 72.0f);
466 // the font height is used for computing the size of an optional legend
467 // symbol to be prepended to the text label.
468 Reference< drawing::XShape > xSymbol;
469 if(pLabel->ShowLegendSymbol)
471 sal_Int32 nSymbolHeight = static_cast< sal_Int32 >( fViewFontSize * 0.6 );
472 awt::Size aCurrentRatio = getPreferredLegendKeyAspectRatio();
473 sal_Int32 nSymbolWidth = aCurrentRatio.Width;
474 if( aCurrentRatio.Height > 0 )
476 nSymbolWidth = nSymbolHeight* aCurrentRatio.Width/aCurrentRatio.Height;
478 awt::Size aMaxSymbolExtent( nSymbolWidth, nSymbolHeight );
480 if( rDataSeries.isVaryColorsByPoint() )
481 xSymbol.set( VSeriesPlotter::createLegendSymbolForPoint( aMaxSymbolExtent, rDataSeries, nPointIndex, xTarget_, m_xShapeFactory ) );
482 else
483 xSymbol.set( VSeriesPlotter::createLegendSymbolForSeries( aMaxSymbolExtent, rDataSeries, xTarget_, m_xShapeFactory ) );
486 //prepare text
487 OUStringBuffer aText;
488 OUString aSeparator(" ");
489 double fRotationDegrees = 0.0;
492 uno::Reference< beans::XPropertySet > xPointProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) );
493 if(xPointProps.is())
495 xPointProps->getPropertyValue( "LabelSeparator" ) >>= aSeparator;
496 // Extract the optional text rotation through the
497 // "TextRotation" property attached to the passed data point.
498 xPointProps->getPropertyValue( "TextRotation" ) >>= fRotationDegrees;
501 catch( const uno::Exception& e )
503 ASSERT_EXCEPTION( e );
506 // check if data series entry percent value and absolute value have to
507 // be appended to the text label, and what should be the separator
508 // character (comma, space, new line). In case it is a new line we get
509 // a multi-line label.
510 bool bMultiLineLabel = aSeparator == "\n";
511 sal_Int32 nLineCountForSymbolsize = 0;
513 if(pLabel->ShowCategoryName)
515 if( m_pExplicitCategoriesProvider )
517 Sequence< OUString > aCategories( m_pExplicitCategoriesProvider->getSimpleCategories() );
518 if( nPointIndex >= 0 && nPointIndex < aCategories.getLength() )
520 aText.append( aCategories[nPointIndex] );
521 ++nLineCountForSymbolsize;
526 if(pLabel->ShowNumber)
528 OUString aNumber = getLabelTextForValue(rDataSeries, nPointIndex, fValue, false);
529 if( !aNumber.isEmpty() )
531 if(!aText.isEmpty())
532 aText.append(aSeparator);
533 aText.append(aNumber);
534 ++nLineCountForSymbolsize;
538 if(pLabel->ShowNumberInPercent)
540 if(fSumValue==0.0)
541 fSumValue=1.0;
542 fValue /= fSumValue;
543 if( fValue < 0 )
544 fValue*=-1.0;
546 OUString aPercentage = getLabelTextForValue(rDataSeries, nPointIndex, fValue, true);
547 if( !aPercentage.isEmpty() )
549 if(!aText.isEmpty())
550 aText.append(aSeparator);
551 aText.append(aPercentage);
552 ++nLineCountForSymbolsize;
556 //prepare properties for multipropertyset-interface of shape
557 tNameSequence* pPropNames;
558 tAnySequence* pPropValues;
559 if( !rDataSeries.getTextLabelMultiPropertyLists( nPointIndex, pPropNames, pPropValues ) )
560 return xTextShape;
562 // set text alignment for the text shape to be created.
563 LabelPositionHelper::changeTextAdjustment( *pPropValues, *pPropNames, eAlignment );
565 //create text shape
566 xTextShape = AbstractShapeFactory::getOrCreateShapeFactory(m_xShapeFactory)->
567 createText( xTarget_, aText.makeStringAndClear()
568 , *pPropNames, *pPropValues, AbstractShapeFactory::makeTransformation( aScreenPosition2D ) );
570 if( !xTextShape.is() )
571 return xTextShape;
573 // in case text is rotated, the transformation property of the text
574 // shape is modified.
575 const awt::Point aUnrotatedTextPos( xTextShape->getPosition() );
576 if( fRotationDegrees != 0.0 )
578 const double fDegreesPi( fRotationDegrees * ( F_PI / -180.0 ) );
579 uno::Reference< beans::XPropertySet > xProp( xTextShape, uno::UNO_QUERY );
580 if( xProp.is() )
581 xProp->setPropertyValue( "Transformation", AbstractShapeFactory::makeTransformation( aScreenPosition2D, fDegreesPi ) );
582 LabelPositionHelper::correctPositionForRotation( xTextShape, eAlignment, fRotationDegrees, true /*bRotateAroundCenter*/ );
585 // in case legend symbol has to be displayed, text shape position is
586 // slightly changed.
587 if( xSymbol.is() )
589 const awt::Point aOldTextPos( xTextShape->getPosition() );
590 awt::Point aNewTextPos( aOldTextPos );
592 awt::Point aSymbolPosition( aUnrotatedTextPos );
593 awt::Size aSymbolSize( xSymbol->getSize() );
594 awt::Size aTextSize( xTextShape->getSize() );
596 sal_Int32 nXDiff = aSymbolSize.Width + static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.22 ) );//minimum 1mm
597 if( !bMultiLineLabel || nLineCountForSymbolsize <= 0 )
598 nLineCountForSymbolsize = 1;
599 aSymbolPosition.Y += ((aTextSize.Height/nLineCountForSymbolsize)/4);
601 if(LABEL_ALIGN_LEFT==eAlignment
602 || LABEL_ALIGN_LEFT_TOP==eAlignment
603 || LABEL_ALIGN_LEFT_BOTTOM==eAlignment)
605 aSymbolPosition.X -= nXDiff;
607 else if(LABEL_ALIGN_RIGHT==eAlignment
608 || LABEL_ALIGN_RIGHT_TOP==eAlignment
609 || LABEL_ALIGN_RIGHT_BOTTOM==eAlignment )
611 aNewTextPos.X += nXDiff;
613 else if(LABEL_ALIGN_TOP==eAlignment
614 || LABEL_ALIGN_BOTTOM==eAlignment
615 || LABEL_ALIGN_CENTER==eAlignment )
617 aSymbolPosition.X -= nXDiff/2;
618 aNewTextPos.X += nXDiff/2;
621 xSymbol->setPosition( aSymbolPosition );
622 xTextShape->setPosition( aNewTextPos );
625 catch( const uno::Exception& e )
627 ASSERT_EXCEPTION( e );
630 return xTextShape;
633 namespace
635 double lcl_getErrorBarLogicLength(
636 const uno::Sequence< double > & rData,
637 uno::Reference< beans::XPropertySet > xProp,
638 sal_Int32 nErrorBarStyle,
639 sal_Int32 nIndex,
640 bool bPositive,
641 bool bYError )
643 double fResult;
644 ::rtl::math::setNan( & fResult );
647 switch( nErrorBarStyle )
649 case ::com::sun::star::chart::ErrorBarStyle::NONE:
650 break;
651 case ::com::sun::star::chart::ErrorBarStyle::VARIANCE:
652 fResult = StatisticsHelper::getVariance( rData );
653 break;
654 case ::com::sun::star::chart::ErrorBarStyle::STANDARD_DEVIATION:
655 fResult = StatisticsHelper::getStandardDeviation( rData );
656 break;
657 case ::com::sun::star::chart::ErrorBarStyle::RELATIVE:
659 double fPercent = 0;
660 if( xProp->getPropertyValue( bPositive
661 ? OUString("PositiveError")
662 : OUString("NegativeError") ) >>= fPercent )
664 if( nIndex >=0 && nIndex < rData.getLength() &&
665 ! ::rtl::math::isNan( rData[nIndex] ) &&
666 ! ::rtl::math::isNan( fPercent ))
668 fResult = rData[nIndex] * fPercent / 100.0;
672 break;
673 case ::com::sun::star::chart::ErrorBarStyle::ABSOLUTE:
674 xProp->getPropertyValue( bPositive
675 ? OUString("PositiveError")
676 : OUString("NegativeError") ) >>= fResult;
677 break;
678 case ::com::sun::star::chart::ErrorBarStyle::ERROR_MARGIN:
680 // todo: check if this is really what's called error-margin
681 double fPercent = 0;
682 if( xProp->getPropertyValue( bPositive
683 ? OUString("PositiveError")
684 : OUString("NegativeError") ) >>= fPercent )
686 double fMaxValue;
687 ::rtl::math::setInf(&fMaxValue, true);
688 const double* pValues = rData.getConstArray();
689 for(sal_Int32 i=0; i<rData.getLength(); ++i, ++pValues)
691 if(fMaxValue<*pValues)
692 fMaxValue=*pValues;
694 if( ::rtl::math::isFinite( fMaxValue ) &&
695 ::rtl::math::isFinite( fPercent ))
697 fResult = fMaxValue * fPercent / 100.0;
701 break;
702 case ::com::sun::star::chart::ErrorBarStyle::STANDARD_ERROR:
703 fResult = StatisticsHelper::getStandardError( rData );
704 break;
705 case ::com::sun::star::chart::ErrorBarStyle::FROM_DATA:
707 uno::Reference< chart2::data::XDataSource > xErrorBarData( xProp, uno::UNO_QUERY );
708 if( xErrorBarData.is())
709 fResult = StatisticsHelper::getErrorFromDataSource(
710 xErrorBarData, nIndex, bPositive, bYError);
712 break;
715 catch( const uno::Exception & e )
717 ASSERT_EXCEPTION( e );
720 return fResult;
723 void lcl_AddErrorBottomLine( const drawing::Position3D& rPosition, ::basegfx::B2DVector aMainDirection
724 , drawing::PolyPolygonShape3D& rPoly, sal_Int32 nSequenceIndex )
726 double fFixedWidth = 200.0;
728 aMainDirection.normalize();
729 ::basegfx::B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX());
730 aOrthoDirection.normalize();
732 ::basegfx::B2DVector aAnchor( rPosition.PositionX, rPosition.PositionY );
733 ::basegfx::B2DVector aStart = aAnchor + aOrthoDirection*fFixedWidth/2.0;
734 ::basegfx::B2DVector aEnd = aAnchor - aOrthoDirection*fFixedWidth/2.0;
736 AddPointToPoly( rPoly, drawing::Position3D( aStart.getX(), aStart.getY(), rPosition.PositionZ), nSequenceIndex );
737 AddPointToPoly( rPoly, drawing::Position3D( aEnd.getX(), aEnd.getY(), rPosition.PositionZ), nSequenceIndex );
740 ::basegfx::B2DVector lcl_getErrorBarMainDirection(
741 const drawing::Position3D& rStart
742 , const drawing::Position3D& rBottomEnd
743 , PlottingPositionHelper* pPosHelper
744 , const drawing::Position3D& rUnscaledLogicPosition
745 , bool bYError )
747 ::basegfx::B2DVector aMainDirection = ::basegfx::B2DVector( rStart.PositionX - rBottomEnd.PositionX
748 , rStart.PositionY - rBottomEnd.PositionY );
749 if( !aMainDirection.getLength() )
751 //get logic clip values:
752 double MinX = pPosHelper->getLogicMinX();
753 double MinY = pPosHelper->getLogicMinY();
754 double MaxX = pPosHelper->getLogicMaxX();
755 double MaxY = pPosHelper->getLogicMaxY();
756 double fZ = pPosHelper->getLogicMinZ();
758 if( bYError )
760 //main direction has constant x value
761 MinX = rUnscaledLogicPosition.PositionX;
762 MaxX = rUnscaledLogicPosition.PositionX;
764 else
766 //main direction has constant y value
767 MinY = rUnscaledLogicPosition.PositionY;
768 MaxY = rUnscaledLogicPosition.PositionY;
771 drawing::Position3D aStart = pPosHelper->transformLogicToScene( MinX, MinY, fZ, false );
772 drawing::Position3D aEnd = pPosHelper->transformLogicToScene( MaxX, MaxY, fZ, false );
774 aMainDirection = ::basegfx::B2DVector( aStart.PositionX - aEnd.PositionX
775 , aStart.PositionY - aEnd.PositionY );
777 if( !aMainDirection.getLength() )
779 //@todo
781 return aMainDirection;
784 drawing::Position3D lcl_transformMixedToScene( PlottingPositionHelper* pPosHelper
785 , double fX /*scaled*/, double fY /*unscaled*/, double fZ /*unscaled*/, bool bClip )
787 if(!pPosHelper)
788 return drawing::Position3D(0,0,0);
789 pPosHelper->doLogicScaling( 0,&fY,&fZ );
790 if(bClip)
791 pPosHelper->clipScaledLogicValues( &fX,&fY,&fZ );
792 return pPosHelper->transformScaledLogicToScene( fX, fY, fZ, false );
795 } // anonymous namespace
797 void VSeriesPlotter::createErrorBar(
798 const uno::Reference< drawing::XShapes >& xTarget
799 , const drawing::Position3D& rUnscaledLogicPosition
800 , const uno::Reference< beans::XPropertySet > & xErrorBarProperties
801 , const VDataSeries& rVDataSeries
802 , sal_Int32 nIndex
803 , bool bYError /* = true */
804 , double* pfScaledLogicX
807 if( !ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel, m_nDimension ) )
808 return;
810 if( ! xErrorBarProperties.is())
811 return;
815 bool bShowPositive = false;
816 bool bShowNegative = false;
817 sal_Int32 nErrorBarStyle = ::com::sun::star::chart::ErrorBarStyle::VARIANCE;
819 xErrorBarProperties->getPropertyValue( "ShowPositiveError") >>= bShowPositive;
820 xErrorBarProperties->getPropertyValue( "ShowNegativeError") >>= bShowNegative;
821 xErrorBarProperties->getPropertyValue( "ErrorBarStyle") >>= nErrorBarStyle;
823 if(!bShowPositive && !bShowNegative)
824 return;
826 if(nErrorBarStyle==::com::sun::star::chart::ErrorBarStyle::NONE)
827 return;
829 if (!m_pPosHelper)
830 return;
832 drawing::Position3D aUnscaledLogicPosition(rUnscaledLogicPosition);
833 if(nErrorBarStyle==::com::sun::star::chart::ErrorBarStyle::STANDARD_DEVIATION)
835 if (bYError)
836 aUnscaledLogicPosition.PositionY = rVDataSeries.getYMeanValue();
837 else
838 aUnscaledLogicPosition.PositionX = rVDataSeries.getXMeanValue();
841 bool bCreateNegativeBorder = false;//make a vertical line at the negative end of the error bar
842 bool bCreatePositiveBorder = false;//make a vertical line at the positive end of the error bar
843 drawing::Position3D aMiddle(aUnscaledLogicPosition);
844 const double fX = aUnscaledLogicPosition.PositionX;
845 const double fY = aUnscaledLogicPosition.PositionY;
846 const double fZ = aUnscaledLogicPosition.PositionZ;
847 double fScaledX = fX;
848 if( pfScaledLogicX )
849 fScaledX = *pfScaledLogicX;
850 else
851 m_pPosHelper->doLogicScaling( &fScaledX, 0, 0 );
853 aMiddle = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fY, fZ, true );
855 drawing::Position3D aNegative(aMiddle);
856 drawing::Position3D aPositive(aMiddle);
858 uno::Sequence< double > aData( bYError ? rVDataSeries.getAllY() : rVDataSeries.getAllX() );
860 if( bShowPositive )
862 double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, true, bYError );
863 if( ::rtl::math::isFinite( fLength ) )
865 double fLocalX = fX;
866 double fLocalY = fY;
867 if( bYError )
869 fLocalY+=fLength;
870 aPositive = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ, true );
872 else
874 fLocalX+=fLength;
875 aPositive = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true );
877 bCreatePositiveBorder = m_pPosHelper->isLogicVisible(fLocalX, fLocalY, fZ);
879 else
880 bShowPositive = false;
883 if( bShowNegative )
885 double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, false, bYError );
886 if( ::rtl::math::isFinite( fLength ) )
888 double fLocalX = fX;
889 double fLocalY = fY;
890 if( bYError )
892 fLocalY-=fLength;
893 aNegative = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ, true );
895 else
897 fLocalX-=fLength;
898 aNegative = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true );
900 bCreateNegativeBorder = m_pPosHelper->isLogicVisible( fLocalX, fLocalY, fZ);
902 else
903 bShowNegative = false;
906 if(!bShowPositive && !bShowNegative)
907 return;
909 drawing::PolyPolygonShape3D aPoly;
911 sal_Int32 nSequenceIndex=0;
912 if( bShowNegative )
913 AddPointToPoly( aPoly, aNegative, nSequenceIndex );
914 AddPointToPoly( aPoly, aMiddle, nSequenceIndex );
915 if( bShowPositive )
916 AddPointToPoly( aPoly, aPositive, nSequenceIndex );
918 if( bShowNegative && bCreateNegativeBorder )
920 ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aNegative, m_pPosHelper, aUnscaledLogicPosition, bYError );
921 nSequenceIndex++;
922 lcl_AddErrorBottomLine( aNegative, aMainDirection, aPoly, nSequenceIndex );
924 if( bShowPositive && bCreatePositiveBorder )
926 ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aPositive, m_pPosHelper, aUnscaledLogicPosition, bYError );
927 nSequenceIndex++;
928 lcl_AddErrorBottomLine( aPositive, aMainDirection, aPoly, nSequenceIndex );
931 uno::Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( xTarget, PolyToPointSequence( aPoly) );
932 setMappedProperties( xShape, xErrorBarProperties, PropertyMapper::getPropertyNameMapForLineProperties() );
934 catch( const uno::Exception & e )
936 ASSERT_EXCEPTION( e );
941 void VSeriesPlotter::createErrorBar_X( const drawing::Position3D& rUnscaledLogicPosition
942 , VDataSeries& rVDataSeries, sal_Int32 nPointIndex
943 , const uno::Reference< drawing::XShapes >& xTarget
944 , double* pfScaledLogicX )
946 if(m_nDimension!=2)
947 return;
948 // error bars
949 uno::Reference< beans::XPropertySet > xErrorBarProp(rVDataSeries.getXErrorBarProperties(nPointIndex));
950 if( xErrorBarProp.is())
952 uno::Reference< drawing::XShapes > xErrorBarsGroup_Shapes(
953 this->getErrorBarsGroupShape(rVDataSeries, xTarget, false) );
955 createErrorBar( xErrorBarsGroup_Shapes
956 , rUnscaledLogicPosition, xErrorBarProp
957 , rVDataSeries, nPointIndex
958 , false /* bYError */
959 , pfScaledLogicX );
963 void VSeriesPlotter::createErrorBar_Y( const drawing::Position3D& rUnscaledLogicPosition
964 , VDataSeries& rVDataSeries, sal_Int32 nPointIndex
965 , const uno::Reference< drawing::XShapes >& xTarget
966 , double* pfScaledLogicX )
968 if(m_nDimension!=2)
969 return;
970 // error bars
971 uno::Reference< beans::XPropertySet > xErrorBarProp(rVDataSeries.getYErrorBarProperties(nPointIndex));
972 if( xErrorBarProp.is())
974 uno::Reference< drawing::XShapes > xErrorBarsGroup_Shapes(
975 this->getErrorBarsGroupShape(rVDataSeries, xTarget, true) );
977 createErrorBar( xErrorBarsGroup_Shapes
978 , rUnscaledLogicPosition, xErrorBarProp
979 , rVDataSeries, nPointIndex
980 , true /* bYError */
981 , pfScaledLogicX );
985 void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries& rVDataSeries,
986 const uno::Reference< drawing::XShapes >& xTarget,
987 const uno::Reference< drawing::XShapes >& xEquationTarget,
988 bool bMaySkipPoints )
990 if(m_nDimension!=2)
991 return;
992 uno::Reference< XRegressionCurveContainer > xContainer( rVDataSeries.getModel(), uno::UNO_QUERY );
993 if(!xContainer.is())
994 return;
996 if (!m_pPosHelper)
997 return;
999 uno::Sequence< uno::Reference< XRegressionCurve > > aCurveList = xContainer->getRegressionCurves();
1001 for(sal_Int32 nN=0; nN<aCurveList.getLength(); nN++)
1003 uno::Reference< XRegressionCurveCalculator > xCalculator( aCurveList[nN]->getCalculator() );
1004 if( !xCalculator.is())
1005 continue;
1007 uno::Reference< beans::XPropertySet > xProperties( aCurveList[nN], uno::UNO_QUERY );
1009 bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurveList[nN] );
1011 sal_Int32 aDegree = 2;
1012 sal_Int32 aPeriod = 2;
1013 double aExtrapolateForward = 0.0;
1014 double aExtrapolateBackward = 0.0;
1015 bool aForceIntercept = false;
1016 double aInterceptValue = 0.0;
1018 if ( xProperties.is() && !bAverageLine )
1020 xProperties->getPropertyValue( "PolynomialDegree") >>= aDegree;
1021 xProperties->getPropertyValue( "MovingAveragePeriod") >>= aPeriod;
1022 xProperties->getPropertyValue( "ExtrapolateForward") >>= aExtrapolateForward;
1023 xProperties->getPropertyValue( "ExtrapolateBackward") >>= aExtrapolateBackward;
1024 xProperties->getPropertyValue( "ForceIntercept") >>= aForceIntercept;
1025 if (aForceIntercept)
1026 xProperties->getPropertyValue( "InterceptValue") >>= aInterceptValue;
1029 double fChartMinX = m_pPosHelper->getLogicMinX();
1030 double fChartMaxX = m_pPosHelper->getLogicMaxX();
1032 double fMinX = fChartMinX;
1033 double fMaxX = fChartMaxX;
1035 double fPointScale = 1.0;
1037 if( !bAverageLine )
1039 rVDataSeries.getMinMaxXValue(fMinX, fMaxX);
1040 fMaxX += aExtrapolateForward;
1041 fMinX -= aExtrapolateBackward;
1043 fPointScale = (fMaxX - fMinX) / (fChartMaxX - fChartMinX);
1046 xCalculator->setRegressionProperties(aDegree, aForceIntercept, aInterceptValue, aPeriod);
1047 xCalculator->recalculateRegression( rVDataSeries.getAllX(), rVDataSeries.getAllY() );
1048 sal_Int32 nPointCount = 100 * fPointScale;
1050 if ( nPointCount < 2 )
1051 nPointCount = 2;
1053 std::vector< ExplicitScaleData > aScales( m_pPosHelper->getScales());
1054 uno::Reference< chart2::XScaling > xScalingX;
1055 uno::Reference< chart2::XScaling > xScalingY;
1056 if( aScales.size() >= 2 )
1058 xScalingX.set( aScales[0].Scaling );
1059 xScalingY.set( aScales[1].Scaling );
1062 uno::Sequence< geometry::RealPoint2D > aCalculatedPoints(
1063 xCalculator->getCurveValues(
1064 fMinX, fMaxX, nPointCount,
1065 xScalingX, xScalingY, bMaySkipPoints ));
1067 nPointCount = aCalculatedPoints.getLength();
1069 drawing::PolyPolygonShape3D aRegressionPoly;
1070 aRegressionPoly.SequenceX.realloc(1);
1071 aRegressionPoly.SequenceY.realloc(1);
1072 aRegressionPoly.SequenceZ.realloc(1);
1073 aRegressionPoly.SequenceX[0].realloc(nPointCount);
1074 aRegressionPoly.SequenceY[0].realloc(nPointCount);
1075 aRegressionPoly.SequenceZ[0].realloc(nPointCount);
1077 sal_Int32 nRealPointCount = 0;
1079 for(sal_Int32 nP = 0; nP < aCalculatedPoints.getLength(); ++nP)
1081 double fLogicX = aCalculatedPoints[nP].X;
1082 double fLogicY = aCalculatedPoints[nP].Y;
1083 double fLogicZ = 0.0; //dummy
1085 // fdo#51656: don't scale mean value lines
1086 if(!bAverageLine)
1087 m_pPosHelper->doLogicScaling( &fLogicX, &fLogicY, &fLogicZ );
1089 if(!rtl::math::isNan(fLogicX) && !rtl::math::isInf(fLogicX) &&
1090 !rtl::math::isNan(fLogicY) && !rtl::math::isInf(fLogicY) &&
1091 !rtl::math::isNan(fLogicZ) && !rtl::math::isInf(fLogicZ) )
1093 aRegressionPoly.SequenceX[0][nRealPointCount] = fLogicX;
1094 aRegressionPoly.SequenceY[0][nRealPointCount] = fLogicY;
1095 nRealPointCount++;
1098 aRegressionPoly.SequenceX[0].realloc(nRealPointCount);
1099 aRegressionPoly.SequenceY[0].realloc(nRealPointCount);
1100 aRegressionPoly.SequenceZ[0].realloc(nRealPointCount);
1102 drawing::PolyPolygonShape3D aClippedPoly;
1103 Clipping::clipPolygonAtRectangle( aRegressionPoly, m_pPosHelper->getScaledLogicClipDoubleRect(), aClippedPoly );
1104 aRegressionPoly = aClippedPoly;
1105 m_pPosHelper->transformScaledLogicToScene( aRegressionPoly );
1107 awt::Point aDefaultPos;
1108 if( aRegressionPoly.SequenceX.getLength() && aRegressionPoly.SequenceX[0].getLength() )
1110 VLineProperties aVLineProperties;
1111 aVLineProperties.initFromPropertySet( xProperties );
1113 //create an extra group shape for each curve for selection handling
1114 uno::Reference< drawing::XShapes > xRegressionGroupShapes =
1115 createGroupShape( xTarget, rVDataSeries.getDataCurveCID( nN, bAverageLine ) );
1116 uno::Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D(
1117 xRegressionGroupShapes, PolyToPointSequence( aRegressionPoly ), &aVLineProperties );
1118 AbstractShapeFactory::setShapeName( xShape, "MarkHandles" );
1119 aDefaultPos = xShape->getPosition();
1122 // curve equation and correlation coefficient
1123 uno::Reference< beans::XPropertySet > xEquationProperties( aCurveList[nN]->getEquationProperties());
1124 if( xEquationProperties.is())
1126 createRegressionCurveEquationShapes(
1127 rVDataSeries.getDataCurveEquationCID( nN ),
1128 xEquationProperties, xEquationTarget, xCalculator,
1129 aDefaultPos );
1134 void VSeriesPlotter::createRegressionCurveEquationShapes(
1135 const OUString & rEquationCID,
1136 const uno::Reference< beans::XPropertySet > & xEquationProperties,
1137 const uno::Reference< drawing::XShapes >& xEquationTarget,
1138 const uno::Reference< chart2::XRegressionCurveCalculator > & xRegressionCurveCalculator,
1139 awt::Point aDefaultPos )
1141 OSL_ASSERT( xEquationProperties.is());
1142 if( !xEquationProperties.is())
1143 return;
1145 bool bShowEquation = false;
1146 bool bShowCorrCoeff = false;
1147 OUString aSep( "\n" );
1148 if(( xEquationProperties->getPropertyValue( "ShowEquation") >>= bShowEquation ) &&
1149 ( xEquationProperties->getPropertyValue( "ShowCorrelationCoefficient") >>= bShowCorrCoeff ))
1151 if( ! (bShowEquation || bShowCorrCoeff))
1152 return;
1154 OUStringBuffer aFormula;
1155 sal_Int32 nNumberFormatKey = 0;
1156 xEquationProperties->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormatKey;
1158 if( bShowEquation )
1160 if( m_apNumberFormatterWrapper.get())
1162 aFormula = xRegressionCurveCalculator->getFormattedRepresentation(
1163 m_apNumberFormatterWrapper->getNumberFormatsSupplier(),
1164 nNumberFormatKey );
1166 else
1168 aFormula = xRegressionCurveCalculator->getRepresentation();
1171 if( bShowCorrCoeff )
1173 aFormula.append( aSep );
1176 if( bShowCorrCoeff )
1178 aFormula.append( "R" );
1179 aFormula.append( sal_Unicode( 0x00b2 ));
1180 aFormula.append( " = ");
1181 double fR( xRegressionCurveCalculator->getCorrelationCoefficient());
1182 if( m_apNumberFormatterWrapper.get())
1184 sal_Int32 nLabelCol = 0;
1185 bool bColChanged;
1186 aFormula.append(
1187 m_apNumberFormatterWrapper->getFormattedString(
1188 nNumberFormatKey, fR*fR, nLabelCol, bColChanged ));
1189 //@todo: change color of label if bColChanged is true
1191 else
1193 sal_Unicode aDecimalSep( '.' );//@todo get this locale dependent
1194 aFormula.append( ::rtl::math::doubleToUString(
1195 fR*fR, rtl_math_StringFormat_G, 4, aDecimalSep, true ));
1199 awt::Point aScreenPosition2D;
1200 chart2::RelativePosition aRelativePosition;
1201 if( xEquationProperties->getPropertyValue( "RelativePosition") >>= aRelativePosition )
1203 //@todo decide whether x is primary or secondary
1204 double fX = aRelativePosition.Primary*m_aPageReferenceSize.Width;
1205 double fY = aRelativePosition.Secondary*m_aPageReferenceSize.Height;
1206 aScreenPosition2D.X = static_cast< sal_Int32 >( ::rtl::math::round( fX ));
1207 aScreenPosition2D.Y = static_cast< sal_Int32 >( ::rtl::math::round( fY ));
1209 else
1210 aScreenPosition2D = aDefaultPos;
1212 if( !aFormula.isEmpty())
1214 // set fill and line properties on creation
1215 tNameSequence aNames;
1216 tAnySequence aValues;
1217 PropertyMapper::getPreparedTextShapePropertyLists( xEquationProperties, aNames, aValues );
1219 uno::Reference< drawing::XShape > xTextShape = m_pShapeFactory->createText(
1220 xEquationTarget, aFormula.makeStringAndClear(),
1221 aNames, aValues, AbstractShapeFactory::makeTransformation( aScreenPosition2D ));
1223 OSL_ASSERT( xTextShape.is());
1224 if( xTextShape.is())
1226 AbstractShapeFactory::setShapeName( xTextShape, rEquationCID );
1227 awt::Size aSize( xTextShape->getSize() );
1228 awt::Point aPos( RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
1229 aScreenPosition2D, aSize, aRelativePosition.Anchor ) );
1230 //ensure that the equation is fully placed within the page (if possible)
1231 if( (aPos.X + aSize.Width) > m_aPageReferenceSize.Width )
1232 aPos.X = m_aPageReferenceSize.Width - aSize.Width;
1233 if( aPos.X < 0 )
1234 aPos.X = 0;
1235 if( (aPos.Y + aSize.Height) > m_aPageReferenceSize.Height )
1236 aPos.Y = m_aPageReferenceSize.Height - aSize.Height;
1237 if( aPos.Y < 0 )
1238 aPos.Y = 0;
1239 xTextShape->setPosition(aPos);
1245 void VSeriesPlotter::setMappedProperties(
1246 const uno::Reference< drawing::XShape >& xTargetShape
1247 , const uno::Reference< beans::XPropertySet >& xSource
1248 , const tPropertyNameMap& rMap
1249 , tPropertyNameValueMap* pOverwriteMap )
1251 uno::Reference< beans::XPropertySet > xTargetProp( xTargetShape, uno::UNO_QUERY );
1252 PropertyMapper::setMappedProperties(xTargetProp,xSource,rMap,pOverwriteMap);
1255 void VSeriesPlotter::setTimeResolutionOnXAxis( long TimeResolution, const Date& rNullDate )
1257 m_nTimeResolution = TimeResolution;
1258 m_aNullDate = rNullDate;
1261 // MinimumAndMaximumSupplier
1262 long VSeriesPlotter::calculateTimeResolutionOnXAxis()
1264 long nRet = ::com::sun::star::chart::TimeUnit::YEAR;
1265 if( m_pExplicitCategoriesProvider )
1267 const std::vector< DatePlusIndex >& rDateCategories = m_pExplicitCategoriesProvider->getDateCategories();
1268 std::vector< DatePlusIndex >::const_iterator aIt = rDateCategories.begin(), aEnd = rDateCategories.end();
1269 Date aNullDate(30,12,1899);
1270 if( m_apNumberFormatterWrapper.get() )
1271 aNullDate = m_apNumberFormatterWrapper->getNullDate();
1272 if( aIt!=aEnd )
1274 Date aPrevious(aNullDate); aPrevious+=static_cast<long>(rtl::math::approxFloor(aIt->fValue));
1275 ++aIt;
1276 for(;aIt!=aEnd;++aIt)
1278 Date aCurrent(aNullDate); aCurrent+=static_cast<long>(rtl::math::approxFloor(aIt->fValue));
1279 if( ::com::sun::star::chart::TimeUnit::YEAR == nRet )
1281 if( DateHelper::IsInSameYear( aPrevious, aCurrent ) )
1282 nRet = ::com::sun::star::chart::TimeUnit::MONTH;
1284 if( ::com::sun::star::chart::TimeUnit::MONTH == nRet )
1286 if( DateHelper::IsInSameMonth( aPrevious, aCurrent ) )
1287 nRet = ::com::sun::star::chart::TimeUnit::DAY;
1289 if( ::com::sun::star::chart::TimeUnit::DAY == nRet )
1290 break;
1291 aPrevious=aCurrent;
1295 return nRet;
1297 double VSeriesPlotter::getMinimumX()
1299 double fMinimum, fMaximum;
1300 getMinimumAndMaximiumX( fMinimum, fMaximum );
1301 return fMinimum;
1303 double VSeriesPlotter::getMaximumX()
1305 double fMinimum, fMaximum;
1306 getMinimumAndMaximiumX( fMinimum, fMaximum );
1307 return fMaximum;
1310 double VSeriesPlotter::getMinimumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex )
1312 if( !m_bCategoryXAxis || ( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() ) )
1314 double fMinY, fMaxY;
1315 this->getMinimumAndMaximiumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex );
1316 return fMinY;
1319 double fMinimum, fMaximum;
1320 ::rtl::math::setInf(&fMinimum, false);
1321 ::rtl::math::setInf(&fMaximum, true);
1322 for(size_t nZ =0; nZ<m_aZSlots.size();nZ++ )
1324 ::std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[nZ];
1325 for(size_t nN =0; nN<rXSlots.size();nN++ )
1327 double fLocalMinimum, fLocalMaximum;
1328 rXSlots[nN].calculateYMinAndMaxForCategoryRange(
1329 static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0
1330 , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0
1331 , isSeparateStackingForDifferentSigns( 1 )
1332 , fLocalMinimum, fLocalMaximum, nAxisIndex );
1333 if(fMaximum<fLocalMaximum)
1334 fMaximum=fLocalMaximum;
1335 if(fMinimum>fLocalMinimum)
1336 fMinimum=fLocalMinimum;
1339 if(::rtl::math::isInf(fMinimum))
1340 ::rtl::math::setNan(&fMinimum);
1341 return fMinimum;
1344 double VSeriesPlotter::getMaximumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex )
1346 if( !m_bCategoryXAxis || ( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() ) )
1348 double fMinY, fMaxY;
1349 this->getMinimumAndMaximiumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex );
1350 return fMaxY;
1353 double fMinimum, fMaximum;
1354 ::rtl::math::setInf(&fMinimum, false);
1355 ::rtl::math::setInf(&fMaximum, true);
1356 for(size_t nZ =0; nZ<m_aZSlots.size();nZ++ )
1358 ::std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[nZ];
1359 for(size_t nN =0; nN<rXSlots.size();nN++ )
1361 double fLocalMinimum, fLocalMaximum;
1362 rXSlots[nN].calculateYMinAndMaxForCategoryRange(
1363 static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0
1364 , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0
1365 , isSeparateStackingForDifferentSigns( 1 )
1366 , fLocalMinimum, fLocalMaximum, nAxisIndex );
1367 if(fMaximum<fLocalMaximum)
1368 fMaximum=fLocalMaximum;
1369 if(fMinimum>fLocalMinimum)
1370 fMinimum=fLocalMinimum;
1373 if(::rtl::math::isInf(fMaximum))
1374 ::rtl::math::setNan(&fMaximum);
1375 return fMaximum;
1378 double VSeriesPlotter::getMinimumZ()
1380 //this is the default for all charts without a meaningfull z axis
1381 return 1.0;
1383 double VSeriesPlotter::getMaximumZ()
1385 if( 3!=m_nDimension || !m_aZSlots.size() )
1386 return getMinimumZ()+1;
1387 return m_aZSlots.size();
1390 namespace
1392 bool lcl_isValueAxis( sal_Int32 nDimensionIndex, bool bCategoryXAxis )
1394 // default implementation: true for Y axes, and for value X axis
1395 if( nDimensionIndex == 0 )
1396 return !bCategoryXAxis;
1397 if( nDimensionIndex == 1 )
1398 return true;
1399 return false;
1403 bool VSeriesPlotter::isExpandBorderToIncrementRhythm( sal_Int32 nDimensionIndex )
1405 return lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis );
1408 bool VSeriesPlotter::isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex )
1410 // do not expand axes in 3D charts
1411 return (m_nDimension < 3) && lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis );
1414 bool VSeriesPlotter::isExpandWideValuesToZero( sal_Int32 nDimensionIndex )
1416 // default implementation: only for Y axis
1417 return nDimensionIndex == 1;
1420 bool VSeriesPlotter::isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex )
1422 // default implementation: only for Y axis
1423 return nDimensionIndex == 1;
1426 bool VSeriesPlotter::isSeparateStackingForDifferentSigns( sal_Int32 nDimensionIndex )
1428 // default implementation: only for Y axis
1429 return nDimensionIndex == 1;
1432 void VSeriesPlotter::getMinimumAndMaximiumX( double& rfMinimum, double& rfMaximum ) const
1434 ::rtl::math::setInf(&rfMinimum, false);
1435 ::rtl::math::setInf(&rfMaximum, true);
1437 ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter = m_aZSlots.begin();
1438 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
1439 for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
1441 ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin();
1442 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1443 for( ; aXSlotIter != aXSlotEnd; ++aXSlotIter )
1445 double fLocalMinimum, fLocalMaximum;
1446 aXSlotIter->getMinimumAndMaximiumX( fLocalMinimum, fLocalMaximum );
1447 if( !::rtl::math::isNan(fLocalMinimum) && fLocalMinimum< rfMinimum )
1448 rfMinimum = fLocalMinimum;
1449 if( !::rtl::math::isNan(fLocalMaximum) && fLocalMaximum> rfMaximum )
1450 rfMaximum = fLocalMaximum;
1453 if(::rtl::math::isInf(rfMinimum))
1454 ::rtl::math::setNan(&rfMinimum);
1455 if(::rtl::math::isInf(rfMaximum))
1456 ::rtl::math::setNan(&rfMaximum);
1459 void VSeriesPlotter::getMinimumAndMaximiumYInContinuousXRange( double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const
1461 ::rtl::math::setInf(&rfMinY, false);
1462 ::rtl::math::setInf(&rfMaxY, true);
1464 ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter = m_aZSlots.begin();
1465 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
1466 for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
1468 ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin();
1469 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1470 for( ; aXSlotIter != aXSlotEnd; ++aXSlotIter )
1472 double fLocalMinimum, fLocalMaximum;
1473 aXSlotIter->getMinimumAndMaximiumYInContinuousXRange( fLocalMinimum, fLocalMaximum, fMinX, fMaxX, nAxisIndex );
1474 if( !::rtl::math::isNan(fLocalMinimum) && fLocalMinimum< rfMinY )
1475 rfMinY = fLocalMinimum;
1476 if( !::rtl::math::isNan(fLocalMaximum) && fLocalMaximum> rfMaxY )
1477 rfMaxY = fLocalMaximum;
1480 if(::rtl::math::isInf(rfMinY))
1481 ::rtl::math::setNan(&rfMinY);
1482 if(::rtl::math::isInf(rfMaxY))
1483 ::rtl::math::setNan(&rfMaxY);
1486 sal_Int32 VSeriesPlotter::getPointCount() const
1488 sal_Int32 nRet = 0;
1490 ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter = m_aZSlots.begin();
1491 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
1493 for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
1495 ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin();
1496 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1498 for( ; aXSlotIter != aXSlotEnd; ++aXSlotIter )
1500 sal_Int32 nPointCount = aXSlotIter->getPointCount();
1501 if( nPointCount>nRet )
1502 nRet = nPointCount;
1505 return nRet;
1508 void VSeriesPlotter::setNumberFormatsSupplier(
1509 const uno::Reference< util::XNumberFormatsSupplier > & xNumFmtSupplier )
1511 m_apNumberFormatterWrapper.reset( new NumberFormatterWrapper( xNumFmtSupplier ));
1514 void VSeriesPlotter::setColorScheme( const uno::Reference< XColorScheme >& xColorScheme )
1516 m_xColorScheme = xColorScheme;
1519 void VSeriesPlotter::setExplicitCategoriesProvider( ExplicitCategoriesProvider* pExplicitCategoriesProvider )
1521 m_pExplicitCategoriesProvider = pExplicitCategoriesProvider;
1524 sal_Int32 VDataSeriesGroup::getPointCount() const
1526 if(!m_bMaxPointCountDirty)
1527 return m_nMaxPointCount;
1529 sal_Int32 nRet = 0;
1530 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = m_aSeriesVector.begin();
1531 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = m_aSeriesVector.end();
1533 for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter)
1535 sal_Int32 nPointCount = (*aSeriesIter)->getTotalPointCount();
1536 if( nPointCount>nRet )
1537 nRet = nPointCount;
1539 m_nMaxPointCount=nRet;
1540 m_aListOfCachedYValues.clear();
1541 m_aListOfCachedYValues.resize(m_nMaxPointCount);
1542 m_bMaxPointCountDirty=false;
1543 return nRet;
1546 sal_Int32 VDataSeriesGroup::getAttachedAxisIndexForFirstSeries() const
1548 sal_Int32 nRet = 0;
1549 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = m_aSeriesVector.begin();
1550 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = m_aSeriesVector.end();
1552 if( aSeriesIter != aSeriesEnd )
1553 nRet = (*aSeriesIter)->getAttachedAxisIndex();
1555 return nRet;
1558 void VDataSeriesGroup::getMinimumAndMaximiumX( double& rfMinimum, double& rfMaximum ) const
1560 const ::std::vector< VDataSeries* >* pSeriesList = &this->m_aSeriesVector;
1562 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin();
1563 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end();
1565 ::rtl::math::setInf(&rfMinimum, false);
1566 ::rtl::math::setInf(&rfMaximum, true);
1568 for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter )
1570 sal_Int32 nPointCount = (*aSeriesIter)->getTotalPointCount();
1571 for(sal_Int32 nN=0;nN<nPointCount;nN++)
1573 double fX = (*aSeriesIter)->getXValue( nN );
1574 if( ::rtl::math::isNan(fX) )
1575 continue;
1576 if(rfMaximum<fX)
1577 rfMaximum=fX;
1578 if(rfMinimum>fX)
1579 rfMinimum=fX;
1582 if(::rtl::math::isInf(rfMinimum))
1583 ::rtl::math::setNan(&rfMinimum);
1584 if(::rtl::math::isInf(rfMaximum))
1585 ::rtl::math::setNan(&rfMaximum);
1588 namespace {
1591 * Keep track of minimum and maximum Y values for one or more data series.
1592 * When multiple data series exist, that indicates that the data series are
1593 * stacked.
1595 * <p>For each X value, we calculate separate Y value ranges for each data
1596 * series in the first pass. In the second pass, we calculate the minimum Y
1597 * value by taking the absolute minimum value of all data series, whereas
1598 * the maxium Y value is the sum of all the series maximum Y values.</p>
1600 * <p>Once that's done for all X values, the final min / max Y values get
1601 * calculated by taking the absolute min / max Y values across all the X
1602 * values.</p>
1604 class PerXMinMaxCalculator
1606 typedef std::pair<double, double> MinMaxType;
1607 typedef std::map<size_t, MinMaxType> SeriesMinMaxType;
1608 typedef boost::ptr_map<double, SeriesMinMaxType> GroupMinMaxType;
1609 typedef std::unordered_map<double, MinMaxType> TotalStoreType;
1610 GroupMinMaxType maSeriesGroup;
1611 size_t mnCurSeries;
1613 public:
1614 PerXMinMaxCalculator() : mnCurSeries(0) {}
1616 void nextSeries() { ++mnCurSeries; }
1618 void setValue(double fX, double fY)
1620 SeriesMinMaxType* pStore = getByXValue(fX); // get storage for given X value.
1621 if (!pStore)
1622 // This shouldn't happen!
1623 return;
1625 SeriesMinMaxType::iterator it = pStore->lower_bound(mnCurSeries);
1626 if (it != pStore->end() && !pStore->key_comp()(mnCurSeries, it->first))
1628 MinMaxType& r = it->second;
1629 // A min-max pair already exists for this series. Update it.
1630 if (fY < r.first)
1631 r.first = fY;
1632 if (r.second < fY)
1633 r.second = fY;
1635 else
1637 // No existing pair. Insert a new one.
1638 pStore->insert(
1639 it, SeriesMinMaxType::value_type(
1640 mnCurSeries, MinMaxType(fY,fY)));
1644 void getTotalRange(double& rfMin, double& rfMax) const
1646 rtl::math::setNan(&rfMin);
1647 rtl::math::setNan(&rfMax);
1649 TotalStoreType aStore;
1650 getTotalStore(aStore);
1652 if (aStore.empty())
1653 return;
1655 TotalStoreType::const_iterator it = aStore.begin(), itEnd = aStore.end();
1656 rfMin = it->second.first;
1657 rfMax = it->second.second;
1658 for (++it; it != itEnd; ++it)
1660 if (rfMin > it->second.first)
1661 rfMin = it->second.first;
1662 if (rfMax < it->second.second)
1663 rfMax = it->second.second;
1667 private:
1669 * Parse all data and reduce them into a set of global Y value ranges per
1670 * X value.
1672 void getTotalStore(TotalStoreType& rStore) const
1674 TotalStoreType aStore;
1675 GroupMinMaxType::const_iterator it = maSeriesGroup.begin(), itEnd = maSeriesGroup.end();
1676 for (; it != itEnd; ++it)
1678 double fX = it->first;
1680 const SeriesMinMaxType& rSeries = *it->second;
1681 SeriesMinMaxType::const_iterator itSeries = rSeries.begin(), itSeriesEnd = rSeries.end();
1682 for (; itSeries != itSeriesEnd; ++itSeries)
1684 double fYMin = itSeries->second.first, fYMax = itSeries->second.second;
1685 TotalStoreType::iterator itr = aStore.find(fX);
1686 if (itr == aStore.end())
1687 // New min-max pair for give X value.
1688 aStore.insert(
1689 TotalStoreType::value_type(fX, std::pair<double,double>(fYMin,fYMax)));
1690 else
1692 MinMaxType& r = itr->second;
1693 if (fYMin < r.first)
1694 r.first = fYMin; // min y-value
1696 r.second += fYMax; // accumulative max y-value.
1700 rStore.swap(aStore);
1703 SeriesMinMaxType* getByXValue(double fX)
1705 GroupMinMaxType::iterator it = maSeriesGroup.find(fX);
1706 if (it == maSeriesGroup.end())
1708 std::pair<GroupMinMaxType::iterator,bool> r =
1709 maSeriesGroup.insert(fX, new SeriesMinMaxType);
1711 if (!r.second)
1712 // insertion failed.
1713 return NULL;
1715 it = r.first;
1718 return it->second;
1724 void VDataSeriesGroup::getMinimumAndMaximiumYInContinuousXRange(
1725 double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const
1727 ::rtl::math::setNan(&rfMinY);
1728 ::rtl::math::setNan(&rfMaxY);
1730 if (m_aSeriesVector.empty())
1731 // No data series. Bail out.
1732 return;
1734 PerXMinMaxCalculator aRangeCalc;
1735 std::vector<VDataSeries*>::const_iterator it = m_aSeriesVector.begin(), itEnd = m_aSeriesVector.end();
1736 for (; it != itEnd; ++it)
1738 const VDataSeries* pSeries = *it;
1739 if (!pSeries)
1740 continue;
1742 for (sal_Int32 i = 0, n = pSeries->getTotalPointCount(); i < n; ++i)
1744 if (nAxisIndex != pSeries->getAttachedAxisIndex())
1745 continue;
1747 double fX = pSeries->getXValue(i);
1748 if (rtl::math::isNan(fX))
1749 continue;
1751 if (fX < fMinX || fX > fMaxX)
1752 // Outside specified X range. Skip it.
1753 continue;
1755 double fY = pSeries->getYValue(i);
1756 if (::rtl::math::isNan(fY))
1757 continue;
1759 aRangeCalc.setValue(fX, fY);
1761 aRangeCalc.nextSeries();
1764 aRangeCalc.getTotalRange(rfMinY, rfMaxY);
1767 void VDataSeriesGroup::calculateYMinAndMaxForCategory( sal_Int32 nCategoryIndex
1768 , bool bSeparateStackingForDifferentSigns
1769 , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex )
1771 ::rtl::math::setInf(&rfMinimumY, false);
1772 ::rtl::math::setInf(&rfMaximumY, true);
1774 sal_Int32 nPointCount = getPointCount();//necessary to create m_aListOfCachedYValues
1775 if(nCategoryIndex<0 || nCategoryIndex>=nPointCount || m_aSeriesVector.empty())
1776 return;
1778 CachedYValues aCachedYValues = m_aListOfCachedYValues[nCategoryIndex][nAxisIndex];
1779 if( !aCachedYValues.m_bValuesDirty )
1781 //return cached values
1782 rfMinimumY = aCachedYValues.m_fMinimumY;
1783 rfMaximumY = aCachedYValues.m_fMaximumY;
1784 return;
1787 double fTotalSum, fPositiveSum, fNegativeSum, fFirstPositiveY, fFirstNegativeY;
1788 ::rtl::math::setNan( &fTotalSum );
1789 ::rtl::math::setNan( &fPositiveSum );
1790 ::rtl::math::setNan( &fNegativeSum );
1791 ::rtl::math::setNan( &fFirstPositiveY );
1792 ::rtl::math::setNan( &fFirstNegativeY );
1794 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = m_aSeriesVector.begin();
1795 ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = m_aSeriesVector.end();
1797 if( bSeparateStackingForDifferentSigns )
1799 for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter )
1801 if( nAxisIndex != (*aSeriesIter)->getAttachedAxisIndex() )
1802 continue;
1804 double fValueMinY = (*aSeriesIter)->getMinimumofAllDifferentYValues( nCategoryIndex );
1805 double fValueMaxY = (*aSeriesIter)->getMaximumofAllDifferentYValues( nCategoryIndex );
1807 if( fValueMaxY >= 0 )
1809 if( ::rtl::math::isNan( fPositiveSum ) )
1810 fPositiveSum = fFirstPositiveY = fValueMaxY;
1811 else
1812 fPositiveSum += fValueMaxY;
1814 if( fValueMinY < 0 )
1816 if(::rtl::math::isNan( fNegativeSum ))
1817 fNegativeSum = fFirstNegativeY = fValueMinY;
1818 else
1819 fNegativeSum += fValueMinY;
1822 rfMinimumY = ::rtl::math::isNan( fNegativeSum ) ? fFirstPositiveY : fNegativeSum;
1823 rfMaximumY = ::rtl::math::isNan( fPositiveSum ) ? fFirstNegativeY : fPositiveSum;
1825 else
1827 for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter )
1829 if( nAxisIndex != (*aSeriesIter)->getAttachedAxisIndex() )
1830 continue;
1832 double fValueMinY = (*aSeriesIter)->getMinimumofAllDifferentYValues( nCategoryIndex );
1833 double fValueMaxY = (*aSeriesIter)->getMaximumofAllDifferentYValues( nCategoryIndex );
1835 if( ::rtl::math::isNan( fTotalSum ) )
1837 rfMinimumY = fValueMinY;
1838 rfMaximumY = fTotalSum = fValueMaxY;
1840 else
1842 fTotalSum += fValueMaxY;
1843 if( rfMinimumY > fTotalSum )
1844 rfMinimumY = fTotalSum;
1845 if( rfMaximumY < fTotalSum )
1846 rfMaximumY = fTotalSum;
1851 aCachedYValues.m_fMinimumY = rfMinimumY;
1852 aCachedYValues.m_fMaximumY = rfMaximumY;
1853 aCachedYValues.m_bValuesDirty = false;
1854 m_aListOfCachedYValues[nCategoryIndex][nAxisIndex]=aCachedYValues;
1857 void VDataSeriesGroup::calculateYMinAndMaxForCategoryRange(
1858 sal_Int32 nStartCategoryIndex, sal_Int32 nEndCategoryIndex
1859 , bool bSeparateStackingForDifferentSigns
1860 , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex )
1862 //@todo maybe cache these values
1863 ::rtl::math::setInf(&rfMinimumY, false);
1864 ::rtl::math::setInf(&rfMaximumY, true);
1866 //iterate through the given categories
1867 if(nStartCategoryIndex<0)
1868 nStartCategoryIndex=0;
1869 if(nEndCategoryIndex<0)
1870 nEndCategoryIndex=0;
1871 for( sal_Int32 nCatIndex = nStartCategoryIndex; nCatIndex <= nEndCategoryIndex; nCatIndex++ )
1873 double fMinimumY; ::rtl::math::setNan(&fMinimumY);
1874 double fMaximumY; ::rtl::math::setNan(&fMaximumY);
1876 this->calculateYMinAndMaxForCategory( nCatIndex
1877 , bSeparateStackingForDifferentSigns, fMinimumY, fMaximumY, nAxisIndex );
1879 if(rfMinimumY > fMinimumY)
1880 rfMinimumY = fMinimumY;
1881 if(rfMaximumY < fMaximumY)
1882 rfMaximumY = fMaximumY;
1886 double VSeriesPlotter::getTransformedDepth() const
1888 double MinZ = m_pMainPosHelper->getLogicMinZ();
1889 double MaxZ = m_pMainPosHelper->getLogicMaxZ();
1890 m_pMainPosHelper->doLogicScaling( 0, 0, &MinZ );
1891 m_pMainPosHelper->doLogicScaling( 0, 0, &MaxZ );
1892 return FIXED_SIZE_FOR_3D_CHART_VOLUME/(MaxZ-MinZ);
1895 void VSeriesPlotter::addSecondaryValueScale( const ExplicitScaleData& rScale, sal_Int32 nAxisIndex )
1896 throw (uno::RuntimeException)
1898 if( nAxisIndex<1 )
1899 return;
1901 m_aSecondaryValueScales[nAxisIndex]=rScale;
1904 PlottingPositionHelper& VSeriesPlotter::getPlottingPositionHelper( sal_Int32 nAxisIndex ) const
1906 PlottingPositionHelper* pRet = 0;
1907 if(nAxisIndex>0)
1909 tSecondaryPosHelperMap::const_iterator aPosIt = m_aSecondaryPosHelperMap.find( nAxisIndex );
1910 if( aPosIt != m_aSecondaryPosHelperMap.end() )
1912 pRet = aPosIt->second;
1914 else if (m_pPosHelper)
1916 tSecondaryValueScales::const_iterator aScaleIt = m_aSecondaryValueScales.find( nAxisIndex );
1917 if( aScaleIt != m_aSecondaryValueScales.end() )
1919 pRet = m_pPosHelper->createSecondaryPosHelper( aScaleIt->second );
1920 m_aSecondaryPosHelperMap[nAxisIndex] = pRet;
1924 if( !pRet )
1925 pRet = m_pMainPosHelper;
1926 if(pRet)
1927 pRet->setTimeResolution( m_nTimeResolution, m_aNullDate );
1928 return *pRet;
1931 void VSeriesPlotter::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& /*rPageSize*/ )
1935 VDataSeries* VSeriesPlotter::getFirstSeries() const
1937 ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter( m_aZSlots.begin() );
1938 ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd( m_aZSlots.end() );
1939 for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
1941 ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin();
1942 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1944 if( aXSlotIter != aXSlotEnd )
1946 VDataSeriesGroup aSeriesGroup( *aXSlotIter );
1947 if( aSeriesGroup.m_aSeriesVector.size() )
1949 VDataSeries* pSeries = aSeriesGroup.m_aSeriesVector[0];
1950 if(pSeries)
1951 return pSeries;
1955 return 0;
1958 uno::Sequence< OUString > VSeriesPlotter::getSeriesNames() const
1960 ::std::vector< OUString > aRetVector;
1962 OUString aRole;
1963 if( m_xChartTypeModel.is() )
1964 aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel();
1966 ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter( m_aZSlots.begin() );
1967 ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd( m_aZSlots.end() );
1968 for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
1970 ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin();
1971 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1973 if( aXSlotIter != aXSlotEnd )
1975 VDataSeriesGroup aSeriesGroup( *aXSlotIter );
1976 if( aSeriesGroup.m_aSeriesVector.size() )
1978 VDataSeries* pSeries = aSeriesGroup.m_aSeriesVector[0];
1979 uno::Reference< XDataSeries > xSeries( pSeries ? pSeries->getModel() : 0 );
1980 if( xSeries.is() )
1982 OUString aSeriesName( DataSeriesHelper::getDataSeriesLabel( xSeries, aRole ) );
1983 aRetVector.push_back( aSeriesName );
1988 return ContainerHelper::ContainerToSequence( aRetVector );
1991 namespace
1993 struct lcl_setRefSizeAtSeriesGroup : public ::std::unary_function< VDataSeriesGroup, void >
1995 lcl_setRefSizeAtSeriesGroup( awt::Size aRefSize ) : m_aRefSize( aRefSize ) {}
1996 void operator()( VDataSeriesGroup & rGroup )
1998 ::std::vector< VDataSeries* >::iterator aIt( rGroup.m_aSeriesVector.begin());
1999 const ::std::vector< VDataSeries* >::iterator aEndIt( rGroup.m_aSeriesVector.end());
2000 for( ; aIt != aEndIt; ++aIt )
2001 (*aIt)->setPageReferenceSize( m_aRefSize );
2004 private:
2005 awt::Size m_aRefSize;
2007 } // anonymous namespace
2009 void VSeriesPlotter::setPageReferenceSize( const ::com::sun::star::awt::Size & rPageRefSize )
2011 m_aPageReferenceSize = rPageRefSize;
2013 // set reference size also at all data series
2015 ::std::vector< VDataSeriesGroup > aSeriesGroups( FlattenVector( m_aZSlots ));
2016 ::std::for_each( aSeriesGroups.begin(), aSeriesGroups.end(),
2017 lcl_setRefSizeAtSeriesGroup( m_aPageReferenceSize ));
2020 //better performance for big data
2021 void VSeriesPlotter::setCoordinateSystemResolution( const Sequence< sal_Int32 >& rCoordinateSystemResolution )
2023 m_aCoordinateSystemResolution = rCoordinateSystemResolution;
2026 bool VSeriesPlotter::WantToPlotInFrontOfAxisLine()
2028 return ChartTypeHelper::isSeriesInFrontOfAxisLine( m_xChartTypeModel );
2031 bool VSeriesPlotter::shouldSnapRectToUsedArea()
2033 if( m_nDimension == 3 )
2034 return false;
2035 return true;
2038 std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntries(
2039 const awt::Size& rEntryKeyAspectRatio
2040 , ::com::sun::star::chart::ChartLegendExpansion eLegendExpansion
2041 , const Reference< beans::XPropertySet >& xTextProperties
2042 , const Reference< drawing::XShapes >& xTarget
2043 , const Reference< lang::XMultiServiceFactory >& xShapeFactory
2044 , const Reference< uno::XComponentContext >& xContext
2047 std::vector< ViewLegendEntry > aResult;
2049 if( xTarget.is() )
2051 //iterate through all series
2052 bool bBreak = false;
2053 bool bFirstSeries = true;
2054 ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin();
2055 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
2056 for( ; aZSlotIter!=aZSlotEnd && !bBreak; ++aZSlotIter )
2058 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
2059 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
2060 for( ; aXSlotIter!=aXSlotEnd && !bBreak; ++aXSlotIter )
2062 ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
2063 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin();
2064 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end();
2065 //iterate through all series in this x slot
2066 for( ; aSeriesIter!=aSeriesEnd && !bBreak; ++aSeriesIter )
2068 VDataSeries* pSeries( *aSeriesIter );
2069 if(!pSeries)
2070 continue;
2072 std::vector< ViewLegendEntry > aSeriesEntries( this->createLegendEntriesForSeries( rEntryKeyAspectRatio,
2073 *pSeries, xTextProperties, xTarget, xShapeFactory, xContext ) );
2075 //add series entries to the result now
2077 // use only the first series if VaryColorsByPoint is set for the first series
2078 if( bFirstSeries && pSeries->isVaryColorsByPoint() )
2079 bBreak = true;
2080 bFirstSeries = false;
2082 // add entries reverse if chart is stacked in y-direction and the legend is not wide.
2083 // If the legend is wide and we have a stacked bar-chart the normal order
2084 // is the correct one
2085 bool bReverse = false;
2086 if( eLegendExpansion != ::com::sun::star::chart::ChartLegendExpansion_WIDE )
2088 StackingDirection eStackingDirection( pSeries->getStackingDirection() );
2089 bReverse = ( eStackingDirection == StackingDirection_Y_STACKING );
2091 //todo: respect direction of axis in future
2094 if(bReverse)
2095 aResult.insert( aResult.begin(), aSeriesEntries.begin(), aSeriesEntries.end() );
2096 else
2097 aResult.insert( aResult.end(), aSeriesEntries.begin(), aSeriesEntries.end() );
2103 return aResult;
2106 ::std::vector< VDataSeries* > VSeriesPlotter::getAllSeries()
2108 ::std::vector< VDataSeries* > aAllSeries;
2109 ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin();
2110 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
2111 for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
2113 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
2114 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
2115 for( ; aXSlotIter != aXSlotEnd; ++aXSlotIter )
2117 ::std::vector< VDataSeries* > aSeriesList = aXSlotIter->m_aSeriesVector;
2118 aAllSeries.insert( aAllSeries.end(), aSeriesList.begin(), aSeriesList.end() );
2121 return aAllSeries;
2124 namespace
2126 bool lcl_HasVisibleLine( const uno::Reference< beans::XPropertySet >& xProps, bool& rbHasDashedLine )
2128 bool bHasVisibleLine = false;
2129 rbHasDashedLine = false;
2130 drawing::LineStyle aLineStyle = drawing::LineStyle_NONE;
2131 if( xProps.is() && ( xProps->getPropertyValue( "LineStyle") >>= aLineStyle ) )
2133 if( aLineStyle != drawing::LineStyle_NONE )
2134 bHasVisibleLine = true;
2135 if( aLineStyle == drawing::LineStyle_DASH )
2136 rbHasDashedLine = true;
2138 return bHasVisibleLine;
2141 bool lcl_HasRegressionCurves( const VDataSeries& rSeries, bool& rbHasDashedLine )
2143 bool bHasRegressionCurves = false;
2144 Reference< XRegressionCurveContainer > xRegrCont( rSeries.getModel(), uno::UNO_QUERY );
2145 if( xRegrCont.is())
2147 Sequence< Reference< XRegressionCurve > > aCurves( xRegrCont->getRegressionCurves() );
2148 sal_Int32 i = 0, nCount = aCurves.getLength();
2149 for( i=0; i<nCount; ++i )
2151 if( aCurves[i].is() )
2153 bHasRegressionCurves = true;
2154 lcl_HasVisibleLine( uno::Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ), rbHasDashedLine );
2158 return bHasRegressionCurves;
2161 LegendSymbolStyle VSeriesPlotter::getLegendSymbolStyle()
2163 return LegendSymbolStyle_BOX;
2166 awt::Size VSeriesPlotter::getPreferredLegendKeyAspectRatio()
2168 awt::Size aRet(1000,1000);
2169 if( m_nDimension==3 )
2170 return aRet;
2172 bool bSeriesAllowsLines = (getLegendSymbolStyle() == LegendSymbolStyle_LINE);
2173 bool bHasLines = false;
2174 bool bHasDashedLines = false;
2175 ::std::vector< VDataSeries* > aAllSeries( getAllSeries() );
2176 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = aAllSeries.begin();
2177 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = aAllSeries.end();
2178 //iterate through all series
2179 for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter )
2181 if( bSeriesAllowsLines )
2183 bool bCurrentDashed = false;
2184 if( lcl_HasVisibleLine( (*aSeriesIter)->getPropertiesOfSeries(), bCurrentDashed ) )
2186 bHasLines = true;
2187 if( bCurrentDashed )
2189 bHasDashedLines = true;
2190 break;
2194 bool bRegressionHasDashedLines=false;
2195 if( lcl_HasRegressionCurves( **aSeriesIter, bRegressionHasDashedLines ) )
2197 bHasLines = true;
2198 if( bRegressionHasDashedLines )
2200 bHasDashedLines = true;
2201 break;
2205 if( bHasLines )
2207 if( bHasDashedLines )
2208 aRet = awt::Size(1600,-1);
2209 else
2210 aRet = awt::Size(800,-1);
2212 return aRet;
2215 uno::Any VSeriesPlotter::getExplicitSymbol( const VDataSeries& /*rSeries*/, sal_Int32 /*nPointIndex*/ )
2217 return uno::Any();
2220 Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForSeries(
2221 const awt::Size& rEntryKeyAspectRatio
2222 , const VDataSeries& rSeries
2223 , const Reference< drawing::XShapes >& xTarget
2224 , const Reference< lang::XMultiServiceFactory >& xShapeFactory )
2227 LegendSymbolStyle eLegendSymbolStyle = this->getLegendSymbolStyle();
2228 uno::Any aExplicitSymbol( this->getExplicitSymbol( rSeries ) );
2230 VLegendSymbolFactory::tPropertyType ePropType =
2231 VLegendSymbolFactory::PROP_TYPE_FILLED_SERIES;
2233 // todo: maybe the property-style does not solely depend on the
2234 // legend-symbol type
2235 switch( eLegendSymbolStyle )
2237 case LegendSymbolStyle_LINE:
2238 ePropType = VLegendSymbolFactory::PROP_TYPE_LINE_SERIES;
2239 break;
2240 default:
2241 break;
2243 Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio,
2244 xTarget, eLegendSymbolStyle, xShapeFactory
2245 , rSeries.getPropertiesOfSeries(), ePropType, aExplicitSymbol ));
2247 return xShape;
2250 Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForPoint(
2251 const awt::Size& rEntryKeyAspectRatio
2252 , const VDataSeries& rSeries
2253 , sal_Int32 nPointIndex
2254 , const Reference< drawing::XShapes >& xTarget
2255 , const Reference< lang::XMultiServiceFactory >& xShapeFactory )
2258 LegendSymbolStyle eLegendSymbolStyle = this->getLegendSymbolStyle();
2259 uno::Any aExplicitSymbol( this->getExplicitSymbol(rSeries,nPointIndex) );
2261 VLegendSymbolFactory::tPropertyType ePropType =
2262 VLegendSymbolFactory::PROP_TYPE_FILLED_SERIES;
2264 // todo: maybe the property-style does not solely depend on the
2265 // legend-symbol type
2266 switch( eLegendSymbolStyle )
2268 case LegendSymbolStyle_LINE:
2269 ePropType = VLegendSymbolFactory::PROP_TYPE_LINE_SERIES;
2270 break;
2271 default:
2272 break;
2275 // the default properties for the data point are the data series properties.
2276 // If a data point has own attributes overwrite them
2277 Reference< beans::XPropertySet > xSeriesProps( rSeries.getPropertiesOfSeries() );
2278 Reference< beans::XPropertySet > xPointSet( xSeriesProps );
2279 if( rSeries.isAttributedDataPoint( nPointIndex ) )
2280 xPointSet.set( rSeries.getPropertiesOfPoint( nPointIndex ));
2282 // if a data point has no own color use a color fom the diagram's color scheme
2283 if( ! rSeries.hasPointOwnColor( nPointIndex ))
2285 Reference< util::XCloneable > xCloneable( xPointSet,uno::UNO_QUERY );
2286 if( xCloneable.is() && m_xColorScheme.is() )
2288 xPointSet.set( xCloneable->createClone(), uno::UNO_QUERY );
2289 Reference< container::XChild > xChild( xPointSet, uno::UNO_QUERY );
2290 if( xChild.is())
2291 xChild->setParent( xSeriesProps );
2293 OSL_ASSERT( xPointSet.is());
2294 xPointSet->setPropertyValue(
2295 "Color", uno::makeAny( m_xColorScheme->getColorByIndex( nPointIndex )));
2299 Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio,
2300 xTarget, eLegendSymbolStyle, xShapeFactory, xPointSet, ePropType, aExplicitSymbol ));
2302 return xShape;
2305 std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries(
2306 const awt::Size& rEntryKeyAspectRatio
2307 , const VDataSeries& rSeries
2308 , const Reference< beans::XPropertySet >& xTextProperties
2309 , const Reference< drawing::XShapes >& xTarget
2310 , const Reference< lang::XMultiServiceFactory >& xShapeFactory
2311 , const Reference< uno::XComponentContext >& xContext
2314 std::vector< ViewLegendEntry > aResult;
2316 if( ! ( xShapeFactory.is() && xTarget.is() && xContext.is() ) )
2317 return aResult;
2321 ViewLegendEntry aEntry;
2322 OUString aLabelText;
2323 bool bVaryColorsByPoint = rSeries.isVaryColorsByPoint();
2324 if( bVaryColorsByPoint )
2326 Sequence< OUString > aCategoryNames;
2327 if( m_pExplicitCategoriesProvider )
2328 aCategoryNames = m_pExplicitCategoriesProvider->getSimpleCategories();
2330 for( sal_Int32 nIdx=0; nIdx<aCategoryNames.getLength(); ++nIdx )
2332 // symbol
2333 uno::Reference< drawing::XShapes > xSymbolGroup( AbstractShapeFactory::getOrCreateShapeFactory(xShapeFactory)->createGroup2D( xTarget ));
2335 // create the symbol
2336 Reference< drawing::XShape > xShape( this->createLegendSymbolForPoint( rEntryKeyAspectRatio,
2337 rSeries, nIdx, xSymbolGroup, xShapeFactory ) );
2339 // set CID to symbol for selection
2340 if( xShape.is() )
2342 aEntry.aSymbol = uno::Reference< drawing::XShape >( xSymbolGroup, uno::UNO_QUERY );
2344 OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_DATA_POINT, nIdx ) );
2345 aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) );
2346 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2347 AbstractShapeFactory::setShapeName( xShape, aCID );
2350 // label
2351 aLabelText = aCategoryNames[nIdx];
2352 if( xShape.is() || !aLabelText.isEmpty() )
2354 aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties );
2355 aResult.push_back(aEntry);
2359 else
2361 // symbol
2362 uno::Reference< drawing::XShapes > xSymbolGroup( AbstractShapeFactory::getOrCreateShapeFactory(xShapeFactory)->createGroup2D( xTarget ));
2364 // create the symbol
2365 Reference< drawing::XShape > xShape( this->createLegendSymbolForSeries(
2366 rEntryKeyAspectRatio, rSeries, xSymbolGroup, xShapeFactory ) );
2368 // set CID to symbol for selection
2369 if( xShape.is())
2371 aEntry.aSymbol = uno::Reference< drawing::XShape >( xSymbolGroup, uno::UNO_QUERY );
2373 OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) );
2374 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2375 AbstractShapeFactory::setShapeName( xShape, aCID );
2378 // label
2379 aLabelText = ( DataSeriesHelper::getDataSeriesLabel( rSeries.getModel(), m_xChartTypeModel.is() ? m_xChartTypeModel->getRoleOfSequenceForSeriesLabel() : "values-y") );
2380 aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties );
2382 aResult.push_back(aEntry);
2385 // don't show legend entry of regression curve & friends if this type of chart
2386 // doesn't support statistics #i63016#, fdo#37197
2387 if (!ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel, m_nDimension ))
2388 return aResult;
2390 Reference< XRegressionCurveContainer > xRegrCont( rSeries.getModel(), uno::UNO_QUERY );
2391 if( xRegrCont.is())
2393 Sequence< Reference< XRegressionCurve > > aCurves( xRegrCont->getRegressionCurves());
2394 sal_Int32 i = 0, nCount = aCurves.getLength();
2395 for( i=0; i<nCount; ++i )
2397 if( aCurves[i].is() )
2399 //label
2400 OUString aResStr( RegressionCurveHelper::getUINameForRegressionCurve( aCurves[i] ) );
2401 replaceParamterInString( aResStr, "%SERIESNAME", aLabelText );
2402 aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aResStr, xTextProperties );
2404 // symbol
2405 uno::Reference< drawing::XShapes > xSymbolGroup( AbstractShapeFactory::getOrCreateShapeFactory(xShapeFactory)->createGroup2D( xTarget ));
2407 // create the symbol
2408 Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio,
2409 xSymbolGroup, LegendSymbolStyle_LINE, xShapeFactory,
2410 Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ),
2411 VLegendSymbolFactory::PROP_TYPE_LINE, uno::Any() ));
2413 // set CID to symbol for selection
2414 if( xShape.is())
2416 aEntry.aSymbol = uno::Reference< drawing::XShape >( xSymbolGroup, uno::UNO_QUERY );
2418 bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurves[i] );
2419 ObjectType eObjectType = bAverageLine ? OBJECTTYPE_DATA_AVERAGE_LINE : OBJECTTYPE_DATA_CURVE;
2420 OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( eObjectType, i ) );
2421 aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) );
2422 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2423 AbstractShapeFactory::setShapeName( xShape, aCID );
2426 aResult.push_back(aEntry);
2431 catch( const uno::Exception & ex )
2433 ASSERT_EXCEPTION( ex );
2435 return aResult;
2438 VSeriesPlotter* VSeriesPlotter::createSeriesPlotter(
2439 const uno::Reference<XChartType>& xChartTypeModel
2440 , sal_Int32 nDimensionCount
2441 , bool bExcludingPositioning )
2443 if (!xChartTypeModel.is())
2444 return NULL;
2446 OUString aChartType = xChartTypeModel->getChartType();
2448 VSeriesPlotter* pRet=NULL;
2449 if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_COLUMN ) )
2450 pRet = new BarChart(xChartTypeModel,nDimensionCount);
2451 else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_BAR ) )
2452 pRet = new BarChart(xChartTypeModel,nDimensionCount);
2453 else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_AREA ) )
2454 pRet = new AreaChart(xChartTypeModel,nDimensionCount,true);
2455 else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_LINE ) )
2456 pRet = new AreaChart(xChartTypeModel,nDimensionCount,true,true);
2457 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER) )
2458 pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true);
2459 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE) )
2460 pRet = new BubbleChart(xChartTypeModel,nDimensionCount);
2461 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
2462 pRet = new PieChart(xChartTypeModel,nDimensionCount, bExcludingPositioning );
2463 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
2464 pRet = new NetChart(xChartTypeModel,nDimensionCount,true,new PolarPlottingPositionHelper());
2465 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
2466 pRet = new NetChart(xChartTypeModel,nDimensionCount,false,new PolarPlottingPositionHelper());
2467 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
2468 pRet = new CandleStickChart(xChartTypeModel,nDimensionCount);
2469 else
2470 pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true);
2471 return pRet;
2474 } //namespace chart
2476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */