Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / chart2 / source / view / charttypes / VSeriesPlotter.cxx
blobea7d5555c8beac47a25b24e93e13f85439134a49
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 <memory>
21 #include <VSeriesPlotter.hxx>
22 #include <AbstractShapeFactory.hxx>
23 #include <chartview/ExplicitValueProvider.hxx>
24 #include <svl/zformat.hxx>
26 #include <CommonConverters.hxx>
27 #include <ViewDefines.hxx>
28 #include <ObjectIdentifier.hxx>
29 #include <StatisticsHelper.hxx>
30 #include <PlottingPositionHelper.hxx>
31 #include <LabelPositionHelper.hxx>
32 #include <ChartTypeHelper.hxx>
33 #include <Clipping.hxx>
34 #include <servicenames_charttypes.hxx>
35 #include <NumberFormatterWrapper.hxx>
36 #include <DataSeriesHelper.hxx>
37 #include <RegressionCurveHelper.hxx>
38 #include <VLegendSymbolFactory.hxx>
39 #include <FormattedStringHelper.hxx>
40 #include <RelativePositionHelper.hxx>
41 #include <DateHelper.hxx>
42 #include <DiagramHelper.hxx>
43 #include <defines.hxx>
45 //only for creation: @todo remove if all plotter are uno components and instantiated via servicefactory
46 #include "BarChart.hxx"
47 #include "PieChart.hxx"
48 #include "AreaChart.hxx"
49 #include "CandleStickChart.hxx"
50 #include "BubbleChart.hxx"
51 #include "NetChart.hxx"
52 #include <unonames.hxx>
53 #include <SpecialCharacters.hxx>
55 #include <com/sun/star/chart/ErrorBarStyle.hpp>
56 #include <com/sun/star/chart/TimeUnit.hpp>
57 #include <com/sun/star/chart2/XDataPointCustomLabelField.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 <o3tl/make_unique.hxx>
64 #include <rtl/ustrbuf.hxx>
65 #include <rtl/math.hxx>
66 #include <basegfx/vector/b2dvector.hxx>
67 #include <com/sun/star/drawing/LineStyle.hpp>
68 #include <com/sun/star/util/XCloneable.hpp>
70 #include <com/sun/star/container/XEnumerationAccess.hpp>
71 #include <com/sun/star/container/XEnumeration.hpp>
72 #include <com/sun/star/lang/XServiceInfo.hpp>
73 #include <com/sun/star/text/XText.hpp>
74 #include <com/sun/star/text/XSimpleText.hpp>
75 #include <com/sun/star/text/XTextContent.hpp>
76 #include <com/sun/star/text/XTextRange.hpp>
77 #include <com/sun/star/text/XTextCursor.hpp>
78 #include <com/sun/star/style/ParagraphAdjust.hpp>
79 #include <com/sun/star/drawing/TextFitToSizeType.hpp>
81 #include <svx/unoshape.hxx>
82 #include <comphelper/sequence.hxx>
83 #include <vcl/svapp.hxx>
84 #include <vcl/settings.hxx>
85 #include <tools/diagnose_ex.h>
87 #include <functional>
88 #include <map>
89 #include <unordered_map>
92 namespace chart {
94 using namespace ::com::sun::star;
95 using namespace ::com::sun::star::chart2;
96 using ::com::sun::star::uno::Reference;
97 using ::com::sun::star::uno::Sequence;
99 VDataSeriesGroup::CachedYValues::CachedYValues()
100 : m_bValuesDirty(true)
101 , m_fMinimumY(0.0)
102 , m_fMaximumY(0.0)
106 VDataSeriesGroup::VDataSeriesGroup( VDataSeries* pSeries )
107 : m_aSeriesVector(1,pSeries)
108 , m_bMaxPointCountDirty(true)
109 , m_nMaxPointCount(0)
110 , m_aListOfCachedYValues()
114 VDataSeriesGroup::~VDataSeriesGroup()
118 void VDataSeriesGroup::deleteSeries()
120 //delete all data series help objects:
121 for (VDataSeries* pSeries : m_aSeriesVector)
123 delete pSeries;
125 m_aSeriesVector.clear();
128 void VDataSeriesGroup::addSeries( VDataSeries* pSeries )
130 m_aSeriesVector.push_back(pSeries);
131 m_bMaxPointCountDirty=true;
134 sal_Int32 VDataSeriesGroup::getSeriesCount() const
136 return m_aSeriesVector.size();
139 VSeriesPlotter::VSeriesPlotter( const uno::Reference<XChartType>& xChartTypeModel
140 , sal_Int32 nDimensionCount, bool bCategoryXAxis )
141 : PlotterBase( nDimensionCount )
142 , m_pMainPosHelper( nullptr )
143 , m_xChartTypeModel(xChartTypeModel)
144 , m_xChartTypeModelProps( uno::Reference< beans::XPropertySet >::query( xChartTypeModel ))
145 , m_aZSlots()
146 , m_bCategoryXAxis(bCategoryXAxis)
147 , m_nTimeResolution(css::chart::TimeUnit::DAY)
148 , m_aNullDate(30,12,1899)
149 , m_xColorScheme()
150 , m_pExplicitCategoriesProvider(nullptr)
151 , m_bPointsWereSkipped(false)
153 SAL_WARN_IF(!m_xChartTypeModel.is(),"chart2","no XChartType available in view, fallback to default values may be wrong");
156 VSeriesPlotter::~VSeriesPlotter()
158 //delete all data series help objects:
159 for (std::vector<VDataSeriesGroup> & rGroupVector : m_aZSlots)
161 for (VDataSeriesGroup & rGroup : rGroupVector)
163 rGroup.deleteSeries();
165 rGroupVector.clear();
167 m_aZSlots.clear();
169 for (auto const& elem : m_aSecondaryPosHelperMap)
171 PlottingPositionHelper* pPosHelper = elem.second;
172 delete pPosHelper;
174 m_aSecondaryPosHelperMap.clear();
176 m_aSecondaryValueScales.clear();
179 void VSeriesPlotter::addSeries( VDataSeries* pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot )
181 //take ownership of pSeries
183 OSL_PRECOND( pSeries, "series to add is NULL" );
184 if(!pSeries)
185 return;
187 if(m_bCategoryXAxis)
189 if( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() )
190 pSeries->setXValues( m_pExplicitCategoriesProvider->getOriginalCategories() );
191 else
192 pSeries->setCategoryXAxis();
194 else
196 if( m_pExplicitCategoriesProvider )
197 pSeries->setXValuesIfNone( m_pExplicitCategoriesProvider->getOriginalCategories() );
200 if(zSlot<0 || zSlot>=static_cast<sal_Int32>(m_aZSlots.size()))
202 //new z slot
203 std::vector< VDataSeriesGroup > aZSlot;
204 aZSlot.emplace_back(pSeries );
205 m_aZSlots.push_back( aZSlot );
207 else
209 //existing zslot
210 std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[zSlot];
212 if(xSlot<0 || xSlot>=static_cast<sal_Int32>(rXSlots.size()))
214 //append the series to already existing x series
215 rXSlots.emplace_back(pSeries );
217 else
219 //x slot is already occupied
220 //y slot decides what to do:
222 VDataSeriesGroup& rYSlots = rXSlots[xSlot];
223 sal_Int32 nYSlotCount = rYSlots.getSeriesCount();
225 if( ySlot < -1 )
227 //move all existing series in the xSlot to next slot
228 //@todo
229 OSL_FAIL( "Not implemented yet");
231 else if( ySlot == -1 || ySlot >= nYSlotCount)
233 //append the series to already existing y series
234 rYSlots.addSeries(pSeries);
236 else
238 //y slot is already occupied
239 //insert at given y and x position
241 //@todo
242 OSL_FAIL( "Not implemented yet");
248 drawing::Direction3D VSeriesPlotter::getPreferredDiagramAspectRatio() const
250 drawing::Direction3D aRet(1.0,1.0,1.0);
251 if (!m_pPosHelper)
252 return aRet;
254 drawing::Direction3D aScale( m_pPosHelper->getScaledLogicWidth() );
255 aRet.DirectionZ = aScale.DirectionZ*0.2;
256 if(aRet.DirectionZ>1.0)
257 aRet.DirectionZ=1.0;
258 if(aRet.DirectionZ>10)
259 aRet.DirectionZ=10;
260 return aRet;
263 void VSeriesPlotter::releaseShapes()
265 for (std::vector<VDataSeriesGroup> const & rGroupVector : m_aZSlots)
267 for (VDataSeriesGroup const & rGroup : rGroupVector)
269 //iterate through all series in this x slot
270 for (VDataSeries* pSeries : rGroup.m_aSeriesVector)
272 pSeries->releaseShapes();
278 uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShape( VDataSeries* pDataSeries
279 , const uno::Reference< drawing::XShapes >& xTarget )
281 uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xGroupShape );
282 if( !xShapes.is() )
284 //create a group shape for this series and add to logic target:
285 xShapes = createGroupShape( xTarget,pDataSeries->getCID() );
286 pDataSeries->m_xGroupShape = xShapes;
288 return xShapes;
291 uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShapeFrontChild( VDataSeries* pDataSeries
292 , const uno::Reference< drawing::XShapes >& xTarget )
294 uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xFrontSubGroupShape );
295 if(!xShapes.is())
297 //ensure that the series group shape is already created
298 uno::Reference< drawing::XShapes > xSeriesShapes( getSeriesGroupShape( pDataSeries, xTarget ) );
299 //ensure that the back child is created first
300 getSeriesGroupShapeBackChild( pDataSeries, xTarget );
301 //use series group shape as parent for the new created front group shape
302 xShapes = createGroupShape( xSeriesShapes );
303 pDataSeries->m_xFrontSubGroupShape = xShapes;
305 return xShapes;
308 uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShapeBackChild( VDataSeries* pDataSeries
309 , const uno::Reference< drawing::XShapes >& xTarget )
311 uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xBackSubGroupShape );
312 if(!xShapes.is())
314 //ensure that the series group shape is already created
315 uno::Reference< drawing::XShapes > xSeriesShapes( getSeriesGroupShape( pDataSeries, xTarget ) );
316 //use series group shape as parent for the new created back group shape
317 xShapes = createGroupShape( xSeriesShapes );
318 pDataSeries->m_xBackSubGroupShape = xShapes;
320 return xShapes;
323 uno::Reference< drawing::XShapes > VSeriesPlotter::getLabelsGroupShape( VDataSeries& rDataSeries
324 , const uno::Reference< drawing::XShapes >& xTextTarget )
326 //xTextTarget needs to be a 2D shape container always!
328 uno::Reference< drawing::XShapes > xShapes( rDataSeries.m_xLabelsGroupShape );
329 if(!xShapes.is())
331 //create a 2D group shape for texts of this series and add to text target:
332 xShapes = m_pShapeFactory->createGroup2D( xTextTarget, rDataSeries.getLabelsCID() );
333 rDataSeries.m_xLabelsGroupShape = xShapes;
335 return xShapes;
338 uno::Reference< drawing::XShapes > VSeriesPlotter::getErrorBarsGroupShape( VDataSeries& rDataSeries
339 , const uno::Reference< drawing::XShapes >& xTarget
340 , bool bYError )
342 uno::Reference< css::drawing::XShapes > &rShapeGroup =
343 bYError ? rDataSeries.m_xErrorYBarsGroupShape : rDataSeries.m_xErrorXBarsGroupShape;
345 uno::Reference< drawing::XShapes > xShapes( rShapeGroup );
346 if(!xShapes.is())
348 //create a group shape for this series and add to logic target:
349 xShapes = createGroupShape( xTarget,rDataSeries.getErrorBarsCID(bYError) );
350 rShapeGroup = xShapes;
352 return xShapes;
356 OUString VSeriesPlotter::getLabelTextForValue( VDataSeries const & rDataSeries
357 , sal_Int32 nPointIndex
358 , double fValue
359 , bool bAsPercentage )
361 OUString aNumber;
363 if( m_apNumberFormatterWrapper.get())
365 sal_Int32 nNumberFormatKey = 0;
366 if( rDataSeries.hasExplicitNumberFormat(nPointIndex,bAsPercentage) )
367 nNumberFormatKey = rDataSeries.getExplicitNumberFormat(nPointIndex,bAsPercentage);
368 else if( bAsPercentage )
370 sal_Int32 nPercentFormat = DiagramHelper::getPercentNumberFormat( m_apNumberFormatterWrapper->getNumberFormatsSupplier() );
371 if( nPercentFormat != -1 )
372 nNumberFormatKey = nPercentFormat;
374 else
376 if( rDataSeries.shouldLabelNumberFormatKeyBeDetectedFromYAxis() && m_aAxesNumberFormats.hasFormat(1,rDataSeries.getAttachedAxisIndex()) ) //y-axis
377 nNumberFormatKey = m_aAxesNumberFormats.getFormat(1,rDataSeries.getAttachedAxisIndex());
378 else
379 nNumberFormatKey = rDataSeries.detectNumberFormatKey( nPointIndex );
381 if(nNumberFormatKey<0)
382 nNumberFormatKey=0;
384 Color nLabelCol;
385 bool bColChanged;
386 aNumber = m_apNumberFormatterWrapper->getFormattedString(
387 nNumberFormatKey, fValue, nLabelCol, bColChanged );
388 //@todo: change color of label if bColChanged is true
390 else
392 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
393 const OUString& aNumDecimalSep = rLocaleDataWrapper.getNumDecimalSep();
394 assert(aNumDecimalSep.getLength() > 0);
395 sal_Unicode cDecSeparator = aNumDecimalSep[0];
396 aNumber = ::rtl::math::doubleToUString( fValue, rtl_math_StringFormat_G /*rtl_math_StringFormat*/
397 , 3/*DecPlaces*/ , cDecSeparator );
399 return aNumber;
402 uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Reference< drawing::XShapes >& xTarget
403 , VDataSeries& rDataSeries
404 , sal_Int32 nPointIndex
405 , double fValue
406 , double fSumValue
407 , const awt::Point& rScreenPosition2D
408 , LabelAlignment eAlignment
409 , sal_Int32 nOffset
410 , sal_Int32 nTextWidth )
412 uno::Reference< drawing::XShape > xTextShape;
413 Sequence<uno::Reference<XDataPointCustomLabelField>> aCustomLabels;
417 const uno::Reference< css::beans::XPropertySet >& xPropertySet(
418 rDataSeries.getPropertiesOfPoint( nPointIndex ) );
419 if( xPropertySet.is() )
421 uno::Any aAny = xPropertySet->getPropertyValue( CHART_UNONAME_CUSTOM_LABEL_FIELDS );
422 if( aAny.hasValue() )
424 aAny >>= aCustomLabels;
428 awt::Point aScreenPosition2D(rScreenPosition2D);
429 if(eAlignment==LABEL_ALIGN_LEFT)
430 aScreenPosition2D.X -= nOffset;
431 else if(eAlignment==LABEL_ALIGN_RIGHT)
432 aScreenPosition2D.X += nOffset;
433 else if(eAlignment==LABEL_ALIGN_TOP)
434 aScreenPosition2D.Y -= nOffset;
435 else if(eAlignment==LABEL_ALIGN_BOTTOM)
436 aScreenPosition2D.Y += nOffset;
438 uno::Reference< drawing::XShapes > xTarget_ =
439 m_pShapeFactory->createGroup2D(
440 getLabelsGroupShape(rDataSeries, xTarget),
441 ObjectIdentifier::createPointCID( rDataSeries.getLabelCID_Stub(), nPointIndex));
443 //check whether the label needs to be created and how:
444 DataPointLabel* pLabel = rDataSeries.getDataPointLabelIfLabel( nPointIndex );
446 if( !pLabel )
447 return xTextShape;
449 //prepare legend symbol
451 // get the font size for the label through the "CharHeight" property
452 // attached to the passed data series entry.
453 // (By tracing font height values it results that for pie chart the
454 // font size is not the same for all labels, but here no font size
455 // modification occurs).
456 float fViewFontSize( 10.0 );
458 uno::Reference< beans::XPropertySet > xProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) );
459 if( xProps.is() )
460 xProps->getPropertyValue( "CharHeight") >>= fViewFontSize;
461 // pt -> 1/100th mm
462 fViewFontSize *= (2540.0f / 72.0f);
465 // the font height is used for computing the size of an optional legend
466 // symbol to be prepended to the text label.
467 Reference< drawing::XShape > xSymbol;
468 if(pLabel->ShowLegendSymbol)
470 sal_Int32 nSymbolHeight = static_cast< sal_Int32 >( fViewFontSize * 0.6 );
471 awt::Size aCurrentRatio = getPreferredLegendKeyAspectRatio();
472 sal_Int32 nSymbolWidth = aCurrentRatio.Width;
473 if( aCurrentRatio.Height > 0 )
475 nSymbolWidth = nSymbolHeight* aCurrentRatio.Width/aCurrentRatio.Height;
477 awt::Size aMaxSymbolExtent( nSymbolWidth, nSymbolHeight );
479 if( rDataSeries.isVaryColorsByPoint() )
480 xSymbol.set( VSeriesPlotter::createLegendSymbolForPoint( aMaxSymbolExtent, rDataSeries, nPointIndex, xTarget_, m_xShapeFactory ) );
481 else
482 xSymbol.set( VSeriesPlotter::createLegendSymbolForSeries( aMaxSymbolExtent, rDataSeries, xTarget_, m_xShapeFactory ) );
485 //prepare text
486 bool bTextWrap = false;
487 OUString aSeparator(" ");
488 double fRotationDegrees = 0.0;
491 uno::Reference< beans::XPropertySet > xPointProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) );
492 if(xPointProps.is())
494 xPointProps->getPropertyValue( "TextWordWrap" ) >>= bTextWrap;
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 SAL_WARN("chart2", "Exception caught. " << e );
506 sal_Int32 nLineCountForSymbolsize = 0;
507 sal_uInt32 nTextListLength = 3;
508 sal_uInt32 nCustomLabelsCount = aCustomLabels.getLength();
509 bool bUseCustomLabel = false;
510 Sequence< OUString > aTextList( nTextListLength );
512 bUseCustomLabel = nCustomLabelsCount > 0;
513 if( bUseCustomLabel )
515 nTextListLength = ( nCustomLabelsCount > 3 ) ? nCustomLabelsCount : 3;
516 aSeparator = "";
517 aTextList = Sequence< OUString >( nTextListLength );
518 for( sal_uInt32 i = 0; i < nCustomLabelsCount; ++i )
520 switch( aCustomLabels[i]->getFieldType() )
522 case DataPointCustomLabelFieldType_VALUE:
524 aTextList[i] = getLabelTextForValue( rDataSeries, nPointIndex, fValue, false );
525 break;
527 case DataPointCustomLabelFieldType_CATEGORYNAME:
529 aTextList[i] = getCategoryName( nPointIndex );
530 break;
532 case DataPointCustomLabelFieldType_SERIESNAME:
534 OUString aRole;
535 if ( m_xChartTypeModel )
536 aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel();
537 uno::Reference< XDataSeries > xSeries( rDataSeries.getModel() );
538 aTextList[i] = DataSeriesHelper::getDataSeriesLabel( xSeries, aRole );
539 break;
541 case DataPointCustomLabelFieldType_CELLREF:
543 // TODO: for now doesn't show placeholder
544 aTextList[i] = OUString();
545 break;
547 case DataPointCustomLabelFieldType_TEXT:
549 aTextList[i] = aCustomLabels[i]->getString();
550 break;
552 case DataPointCustomLabelFieldType_NEWLINE:
554 aTextList[i] = "\n";
555 break;
557 default:
558 break;
560 aCustomLabels[i]->setString( aTextList[i] );
563 else
565 if( pLabel->ShowCategoryName )
567 aTextList[0] = getCategoryName( nPointIndex );
570 if( pLabel->ShowNumber )
572 aTextList[1] = getLabelTextForValue(rDataSeries, nPointIndex, fValue, false);
575 if( pLabel->ShowNumberInPercent )
577 if(fSumValue==0.0)
578 fSumValue=1.0;
579 fValue /= fSumValue;
580 if( fValue < 0 )
581 fValue*=-1.0;
583 aTextList[2] = getLabelTextForValue(rDataSeries, nPointIndex, fValue, true);
587 for( sal_Int32 nN = 0; nN < aTextList.getLength(); ++nN )
589 if( !aTextList[nN].isEmpty() )
591 ++nLineCountForSymbolsize;
595 //prepare properties for multipropertyset-interface of shape
596 tNameSequence* pPropNames;
597 tAnySequence* pPropValues;
598 if( !rDataSeries.getTextLabelMultiPropertyLists( nPointIndex, pPropNames, pPropValues ) )
599 return xTextShape;
601 // set text alignment for the text shape to be created.
602 LabelPositionHelper::changeTextAdjustment( *pPropValues, *pPropNames, eAlignment );
604 // check if data series entry percent value and absolute value have to
605 // be appended to the text label, and what should be the separator
606 // character (comma, space, new line). In case it is a new line we get
607 // a multi-line label.
608 bool bMultiLineLabel = ( aSeparator == "\n" );
610 if( bUseCustomLabel )
612 Sequence< uno::Reference< XFormattedString > > aFormattedLabels( aCustomLabels.getLength() );
613 for( int i = 0; i < aFormattedLabels.getLength(); i++ )
615 uno::Reference< XFormattedString > xString( aCustomLabels[i], uno::UNO_QUERY );
616 aFormattedLabels[i] = xString;
619 // center the text
620 sal_uInt32 nProperties = pPropNames->getLength();
621 pPropNames->realloc( nProperties + 1 );
622 pPropValues->realloc( nProperties + 1 );
623 (*pPropNames)[ nProperties ] = UNO_NAME_EDIT_PARA_ADJUST;
624 (*pPropValues)[ nProperties ] <<= style::ParagraphAdjust_CENTER;
626 // create text shape
627 xTextShape = AbstractShapeFactory::getOrCreateShapeFactory( m_xShapeFactory )->
628 createText( xTarget_, aFormattedLabels, *pPropNames, *pPropValues,
629 AbstractShapeFactory::makeTransformation( aScreenPosition2D ) );
631 else if( bMultiLineLabel )
633 // prepare properties for each paragraph
634 // we want to have the value and percent value centered respect
635 // with the category name
636 Sequence< tNameSequence > aParaPropNames(3);
637 aParaPropNames[1].realloc(1);
638 aParaPropNames[1][0] = "ParaAdjust";
639 aParaPropNames[2].realloc(1);
640 aParaPropNames[2][0] = "ParaAdjust";
642 Sequence< tAnySequence > aParaPropValues(3);
643 aParaPropValues[1].realloc(1);
644 aParaPropValues[1][0] <<= style::ParagraphAdjust_CENTER;
645 aParaPropValues[2].realloc(1);
646 aParaPropValues[2][0] <<= style::ParagraphAdjust_CENTER;
648 //create text shape
649 xTextShape = AbstractShapeFactory::getOrCreateShapeFactory(m_xShapeFactory)->
650 createText( xTarget_, aTextList, aParaPropNames, aParaPropValues
651 , *pPropNames, *pPropValues, AbstractShapeFactory::makeTransformation( aScreenPosition2D ) );
653 else
655 // join text list elements
656 OUStringBuffer aText;
657 for( sal_uInt32 nN = 0; nN < nTextListLength; ++nN)
659 if( !aTextList[nN].isEmpty() )
661 if( !aText.isEmpty() )
663 aText.append(aSeparator);
665 aText.append( aTextList[nN] );
669 //create text shape
670 xTextShape = AbstractShapeFactory::getOrCreateShapeFactory(m_xShapeFactory)->
671 createText( xTarget_, aText.makeStringAndClear()
672 , *pPropNames, *pPropValues, AbstractShapeFactory::makeTransformation( aScreenPosition2D ) );
675 if( !xTextShape.is() )
676 return xTextShape;
678 // we need to use a default value for the maximum width property ?
679 if( nTextWidth == 0 && bTextWrap )
681 sal_Int32 nMinSize =
682 (m_aPageReferenceSize.Height < m_aPageReferenceSize.Width)
683 ? m_aPageReferenceSize.Height
684 : m_aPageReferenceSize.Width;
685 nTextWidth = nMinSize / 3;
688 // in case text must be wrapped set the maximum width property
689 // for the text shape
690 if( nTextWidth != 0 && bTextWrap )
692 uno::Reference< beans::XPropertySet > xProp( xTextShape, uno::UNO_QUERY );
693 if( xProp.is() )
695 // compute the height of a line of text
696 if( !bMultiLineLabel || nLineCountForSymbolsize <= 0 )
698 nLineCountForSymbolsize = 1;
700 awt::Size aTextSize = xTextShape->getSize();
701 sal_Int32 aTextLineHeight = aTextSize.Height / nLineCountForSymbolsize;
703 // set maximum text width
704 uno::Any aTextMaximumFrameWidth( nTextWidth );
705 xProp->setPropertyValue( "TextMaximumFrameWidth", aTextMaximumFrameWidth );
707 // compute the total lines of text
708 aTextSize = xTextShape->getSize();
709 nLineCountForSymbolsize = aTextSize.Height / aTextLineHeight;
713 // in case text is rotated, the transformation property of the text
714 // shape is modified.
715 const awt::Point aUnrotatedTextPos( xTextShape->getPosition() );
716 if( fRotationDegrees != 0.0 )
718 const double fDegreesPi( fRotationDegrees * ( F_PI / -180.0 ) );
719 uno::Reference< beans::XPropertySet > xProp( xTextShape, uno::UNO_QUERY );
720 if( xProp.is() )
721 xProp->setPropertyValue( "Transformation", AbstractShapeFactory::makeTransformation( aScreenPosition2D, fDegreesPi ) );
722 LabelPositionHelper::correctPositionForRotation( xTextShape, eAlignment, fRotationDegrees, true /*bRotateAroundCenter*/ );
725 // in case legend symbol has to be displayed, text shape position is
726 // slightly changed.
727 if( xSymbol.is() )
729 const awt::Point aOldTextPos( xTextShape->getPosition() );
730 awt::Point aNewTextPos( aOldTextPos );
732 awt::Point aSymbolPosition( aUnrotatedTextPos );
733 awt::Size aSymbolSize( xSymbol->getSize() );
734 awt::Size aTextSize = xTextShape->getSize();
736 sal_Int32 nXDiff = aSymbolSize.Width + static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.22 ) );//minimum 1mm
737 if( !bMultiLineLabel || nLineCountForSymbolsize <= 0 )
738 nLineCountForSymbolsize = 1;
739 aSymbolPosition.Y += ((aTextSize.Height/nLineCountForSymbolsize)/4);
741 if(eAlignment==LABEL_ALIGN_LEFT
742 || eAlignment==LABEL_ALIGN_LEFT_TOP
743 || eAlignment==LABEL_ALIGN_LEFT_BOTTOM)
745 aSymbolPosition.X -= nXDiff;
747 else if(eAlignment==LABEL_ALIGN_RIGHT
748 || eAlignment==LABEL_ALIGN_RIGHT_TOP
749 || eAlignment==LABEL_ALIGN_RIGHT_BOTTOM )
751 aNewTextPos.X += nXDiff;
753 else if(eAlignment==LABEL_ALIGN_TOP
754 || eAlignment==LABEL_ALIGN_BOTTOM
755 || eAlignment==LABEL_ALIGN_CENTER )
757 aSymbolPosition.X -= nXDiff/2;
758 aNewTextPos.X += nXDiff/2;
761 xSymbol->setPosition( aSymbolPosition );
762 xTextShape->setPosition( aNewTextPos );
765 catch( const uno::Exception& e )
767 SAL_WARN("chart2", "Exception caught. " << e );
770 return xTextShape;
773 namespace
775 double lcl_getErrorBarLogicLength(
776 const uno::Sequence< double > & rData,
777 const uno::Reference< beans::XPropertySet >& xProp,
778 sal_Int32 nErrorBarStyle,
779 sal_Int32 nIndex,
780 bool bPositive,
781 bool bYError )
783 double fResult;
784 ::rtl::math::setNan( & fResult );
787 switch( nErrorBarStyle )
789 case css::chart::ErrorBarStyle::NONE:
790 break;
791 case css::chart::ErrorBarStyle::VARIANCE:
792 fResult = StatisticsHelper::getVariance( rData );
793 break;
794 case css::chart::ErrorBarStyle::STANDARD_DEVIATION:
795 fResult = StatisticsHelper::getStandardDeviation( rData );
796 break;
797 case css::chart::ErrorBarStyle::RELATIVE:
799 double fPercent = 0;
800 if( xProp->getPropertyValue( bPositive
801 ? OUString("PositiveError")
802 : OUString("NegativeError") ) >>= fPercent )
804 if( nIndex >=0 && nIndex < rData.getLength() &&
805 ! ::rtl::math::isNan( rData[nIndex] ) &&
806 ! ::rtl::math::isNan( fPercent ))
808 fResult = rData[nIndex] * fPercent / 100.0;
812 break;
813 case css::chart::ErrorBarStyle::ABSOLUTE:
814 xProp->getPropertyValue( bPositive
815 ? OUString("PositiveError")
816 : OUString("NegativeError") ) >>= fResult;
817 break;
818 case css::chart::ErrorBarStyle::ERROR_MARGIN:
820 // todo: check if this is really what's called error-margin
821 double fPercent = 0;
822 if( xProp->getPropertyValue( bPositive
823 ? OUString("PositiveError")
824 : OUString("NegativeError") ) >>= fPercent )
826 double fMaxValue;
827 ::rtl::math::setInf(&fMaxValue, true);
828 const double* pValues = rData.getConstArray();
829 for(sal_Int32 i=0; i<rData.getLength(); ++i, ++pValues)
831 if(fMaxValue<*pValues)
832 fMaxValue=*pValues;
834 if( ::rtl::math::isFinite( fMaxValue ) &&
835 ::rtl::math::isFinite( fPercent ))
837 fResult = fMaxValue * fPercent / 100.0;
841 break;
842 case css::chart::ErrorBarStyle::STANDARD_ERROR:
843 fResult = StatisticsHelper::getStandardError( rData );
844 break;
845 case css::chart::ErrorBarStyle::FROM_DATA:
847 uno::Reference< chart2::data::XDataSource > xErrorBarData( xProp, uno::UNO_QUERY );
848 if( xErrorBarData.is())
849 fResult = StatisticsHelper::getErrorFromDataSource(
850 xErrorBarData, nIndex, bPositive, bYError);
852 break;
855 catch( const uno::Exception & e )
857 SAL_WARN("chart2", "Exception caught. " << e );
860 return fResult;
863 void lcl_AddErrorBottomLine( const drawing::Position3D& rPosition, ::basegfx::B2DVector aMainDirection
864 , drawing::PolyPolygonShape3D& rPoly, sal_Int32 nSequenceIndex )
866 double fFixedWidth = 200.0;
868 aMainDirection.normalize();
869 ::basegfx::B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX());
870 aOrthoDirection.normalize();
872 ::basegfx::B2DVector aAnchor( rPosition.PositionX, rPosition.PositionY );
873 ::basegfx::B2DVector aStart = aAnchor + aOrthoDirection*fFixedWidth/2.0;
874 ::basegfx::B2DVector aEnd = aAnchor - aOrthoDirection*fFixedWidth/2.0;
876 AddPointToPoly( rPoly, drawing::Position3D( aStart.getX(), aStart.getY(), rPosition.PositionZ), nSequenceIndex );
877 AddPointToPoly( rPoly, drawing::Position3D( aEnd.getX(), aEnd.getY(), rPosition.PositionZ), nSequenceIndex );
880 ::basegfx::B2DVector lcl_getErrorBarMainDirection(
881 const drawing::Position3D& rStart
882 , const drawing::Position3D& rBottomEnd
883 , PlottingPositionHelper const * pPosHelper
884 , const drawing::Position3D& rUnscaledLogicPosition
885 , bool bYError )
887 ::basegfx::B2DVector aMainDirection = ::basegfx::B2DVector( rStart.PositionX - rBottomEnd.PositionX
888 , rStart.PositionY - rBottomEnd.PositionY );
889 if( !aMainDirection.getLength() )
891 //get logic clip values:
892 double MinX = pPosHelper->getLogicMinX();
893 double MinY = pPosHelper->getLogicMinY();
894 double MaxX = pPosHelper->getLogicMaxX();
895 double MaxY = pPosHelper->getLogicMaxY();
896 double fZ = pPosHelper->getLogicMinZ();
898 if( bYError )
900 //main direction has constant x value
901 MinX = rUnscaledLogicPosition.PositionX;
902 MaxX = rUnscaledLogicPosition.PositionX;
904 else
906 //main direction has constant y value
907 MinY = rUnscaledLogicPosition.PositionY;
908 MaxY = rUnscaledLogicPosition.PositionY;
911 drawing::Position3D aStart = pPosHelper->transformLogicToScene( MinX, MinY, fZ, false );
912 drawing::Position3D aEnd = pPosHelper->transformLogicToScene( MaxX, MaxY, fZ, false );
914 aMainDirection = ::basegfx::B2DVector( aStart.PositionX - aEnd.PositionX
915 , aStart.PositionY - aEnd.PositionY );
917 if( !aMainDirection.getLength() )
919 //@todo
921 return aMainDirection;
924 drawing::Position3D lcl_transformMixedToScene( PlottingPositionHelper const * pPosHelper
925 , double fX /*scaled*/, double fY /*unscaled*/, double fZ /*unscaled*/ )
927 if(!pPosHelper)
928 return drawing::Position3D(0,0,0);
929 pPosHelper->doLogicScaling( nullptr,&fY,&fZ );
930 pPosHelper->clipScaledLogicValues( &fX,&fY,&fZ );
931 return pPosHelper->transformScaledLogicToScene( fX, fY, fZ, false );
934 } // anonymous namespace
936 void VSeriesPlotter::createErrorBar(
937 const uno::Reference< drawing::XShapes >& xTarget
938 , const drawing::Position3D& rUnscaledLogicPosition
939 , const uno::Reference< beans::XPropertySet > & xErrorBarProperties
940 , const VDataSeries& rVDataSeries
941 , sal_Int32 nIndex
942 , bool bYError /* = true */
943 , const double* pfScaledLogicX
946 if( !ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel, m_nDimension ) )
947 return;
949 if( ! xErrorBarProperties.is())
950 return;
954 bool bShowPositive = false;
955 bool bShowNegative = false;
956 sal_Int32 nErrorBarStyle = css::chart::ErrorBarStyle::VARIANCE;
958 xErrorBarProperties->getPropertyValue( "ShowPositiveError") >>= bShowPositive;
959 xErrorBarProperties->getPropertyValue( "ShowNegativeError") >>= bShowNegative;
960 xErrorBarProperties->getPropertyValue( "ErrorBarStyle") >>= nErrorBarStyle;
962 if(!bShowPositive && !bShowNegative)
963 return;
965 if(nErrorBarStyle==css::chart::ErrorBarStyle::NONE)
966 return;
968 if (!m_pPosHelper)
969 return;
971 drawing::Position3D aUnscaledLogicPosition(rUnscaledLogicPosition);
972 if(nErrorBarStyle==css::chart::ErrorBarStyle::STANDARD_DEVIATION)
974 if (bYError)
975 aUnscaledLogicPosition.PositionY = rVDataSeries.getYMeanValue();
976 else
977 aUnscaledLogicPosition.PositionX = rVDataSeries.getXMeanValue();
980 bool bCreateNegativeBorder = false;//make a vertical line at the negative end of the error bar
981 bool bCreatePositiveBorder = false;//make a vertical line at the positive end of the error bar
982 drawing::Position3D aMiddle(aUnscaledLogicPosition);
983 const double fX = aUnscaledLogicPosition.PositionX;
984 const double fY = aUnscaledLogicPosition.PositionY;
985 const double fZ = aUnscaledLogicPosition.PositionZ;
986 double fScaledX = fX;
987 if( pfScaledLogicX )
988 fScaledX = *pfScaledLogicX;
989 else
990 m_pPosHelper->doLogicScaling( &fScaledX, nullptr, nullptr );
992 aMiddle = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fY, fZ );
994 drawing::Position3D aNegative(aMiddle);
995 drawing::Position3D aPositive(aMiddle);
997 uno::Sequence< double > aData( bYError ? rVDataSeries.getAllY() : rVDataSeries.getAllX() );
999 if( bShowPositive )
1001 double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, true, bYError );
1002 if( ::rtl::math::isFinite( fLength ) )
1004 double fLocalX = fX;
1005 double fLocalY = fY;
1006 if( bYError )
1008 fLocalY+=fLength;
1009 aPositive = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ );
1011 else
1013 fLocalX+=fLength;
1014 aPositive = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true );
1016 bCreatePositiveBorder = m_pPosHelper->isLogicVisible(fLocalX, fLocalY, fZ);
1018 else
1019 bShowPositive = false;
1022 if( bShowNegative )
1024 double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, false, bYError );
1025 if( ::rtl::math::isFinite( fLength ) )
1027 double fLocalX = fX;
1028 double fLocalY = fY;
1029 if( bYError )
1031 fLocalY-=fLength;
1032 aNegative = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ );
1034 else
1036 fLocalX-=fLength;
1037 aNegative = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true );
1039 bCreateNegativeBorder = m_pPosHelper->isLogicVisible( fLocalX, fLocalY, fZ);
1041 else
1042 bShowNegative = false;
1045 if(!bShowPositive && !bShowNegative)
1046 return;
1048 drawing::PolyPolygonShape3D aPoly;
1050 sal_Int32 nSequenceIndex=0;
1051 if( bShowNegative )
1052 AddPointToPoly( aPoly, aNegative, nSequenceIndex );
1053 AddPointToPoly( aPoly, aMiddle, nSequenceIndex );
1054 if( bShowPositive )
1055 AddPointToPoly( aPoly, aPositive, nSequenceIndex );
1057 if( bShowNegative && bCreateNegativeBorder )
1059 ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aNegative, m_pPosHelper, aUnscaledLogicPosition, bYError );
1060 nSequenceIndex++;
1061 lcl_AddErrorBottomLine( aNegative, aMainDirection, aPoly, nSequenceIndex );
1063 if( bShowPositive && bCreatePositiveBorder )
1065 ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aPositive, m_pPosHelper, aUnscaledLogicPosition, bYError );
1066 nSequenceIndex++;
1067 lcl_AddErrorBottomLine( aPositive, aMainDirection, aPoly, nSequenceIndex );
1070 uno::Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( xTarget, PolyToPointSequence( aPoly) );
1071 setMappedProperties( xShape, xErrorBarProperties, PropertyMapper::getPropertyNameMapForLineProperties() );
1073 catch( const uno::Exception & e )
1075 SAL_WARN("chart2", "Exception caught. " << e );
1080 void VSeriesPlotter::createErrorBar_X( const drawing::Position3D& rUnscaledLogicPosition
1081 , VDataSeries& rVDataSeries, sal_Int32 nPointIndex
1082 , const uno::Reference< drawing::XShapes >& xTarget )
1084 if(m_nDimension!=2)
1085 return;
1086 // error bars
1087 uno::Reference< beans::XPropertySet > xErrorBarProp(rVDataSeries.getXErrorBarProperties(nPointIndex));
1088 if( xErrorBarProp.is())
1090 uno::Reference< drawing::XShapes > xErrorBarsGroup_Shapes(
1091 getErrorBarsGroupShape(rVDataSeries, xTarget, false) );
1093 createErrorBar( xErrorBarsGroup_Shapes
1094 , rUnscaledLogicPosition, xErrorBarProp
1095 , rVDataSeries, nPointIndex
1096 , false /* bYError */
1097 , nullptr );
1101 void VSeriesPlotter::createErrorBar_Y( const drawing::Position3D& rUnscaledLogicPosition
1102 , VDataSeries& rVDataSeries, sal_Int32 nPointIndex
1103 , const uno::Reference< drawing::XShapes >& xTarget
1104 , double const * pfScaledLogicX )
1106 if(m_nDimension!=2)
1107 return;
1108 // error bars
1109 uno::Reference< beans::XPropertySet > xErrorBarProp(rVDataSeries.getYErrorBarProperties(nPointIndex));
1110 if( xErrorBarProp.is())
1112 uno::Reference< drawing::XShapes > xErrorBarsGroup_Shapes(
1113 getErrorBarsGroupShape(rVDataSeries, xTarget, true) );
1115 createErrorBar( xErrorBarsGroup_Shapes
1116 , rUnscaledLogicPosition, xErrorBarProp
1117 , rVDataSeries, nPointIndex
1118 , true /* bYError */
1119 , pfScaledLogicX );
1123 void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries const & rVDataSeries,
1124 const uno::Reference< drawing::XShapes >& xTarget,
1125 const uno::Reference< drawing::XShapes >& xEquationTarget,
1126 bool bMaySkipPoints )
1128 if(m_nDimension!=2)
1129 return;
1130 uno::Reference< XRegressionCurveContainer > xContainer( rVDataSeries.getModel(), uno::UNO_QUERY );
1131 if(!xContainer.is())
1132 return;
1134 if (!m_pPosHelper)
1135 return;
1137 uno::Sequence< uno::Reference< XRegressionCurve > > aCurveList = xContainer->getRegressionCurves();
1139 for(sal_Int32 nN=0; nN<aCurveList.getLength(); nN++)
1141 uno::Reference< XRegressionCurveCalculator > xCalculator( aCurveList[nN]->getCalculator() );
1142 if( !xCalculator.is())
1143 continue;
1145 uno::Reference< beans::XPropertySet > xProperties( aCurveList[nN], uno::UNO_QUERY );
1147 bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurveList[nN] );
1149 sal_Int32 aDegree = 2;
1150 sal_Int32 aPeriod = 2;
1151 double aExtrapolateForward = 0.0;
1152 double aExtrapolateBackward = 0.0;
1153 bool bForceIntercept = false;
1154 double aInterceptValue = 0.0;
1156 if ( xProperties.is() && !bAverageLine )
1158 xProperties->getPropertyValue( "PolynomialDegree") >>= aDegree;
1159 xProperties->getPropertyValue( "MovingAveragePeriod") >>= aPeriod;
1160 xProperties->getPropertyValue( "ExtrapolateForward") >>= aExtrapolateForward;
1161 xProperties->getPropertyValue( "ExtrapolateBackward") >>= aExtrapolateBackward;
1162 xProperties->getPropertyValue( "ForceIntercept") >>= bForceIntercept;
1163 if (bForceIntercept)
1164 xProperties->getPropertyValue( "InterceptValue") >>= aInterceptValue;
1167 double fChartMinX = m_pPosHelper->getLogicMinX();
1168 double fChartMaxX = m_pPosHelper->getLogicMaxX();
1170 double fMinX = fChartMinX;
1171 double fMaxX = fChartMaxX;
1173 double fPointScale = 1.0;
1175 if( !bAverageLine )
1177 rVDataSeries.getMinMaxXValue(fMinX, fMaxX);
1178 fMaxX += aExtrapolateForward;
1179 fMinX -= aExtrapolateBackward;
1181 fPointScale = (fMaxX - fMinX) / (fChartMaxX - fChartMinX);
1182 // sanitize the value, tdf#119922
1183 fPointScale = std::min(fPointScale, 1000.0);
1186 xCalculator->setRegressionProperties(aDegree, bForceIntercept, aInterceptValue, aPeriod);
1187 xCalculator->recalculateRegression( rVDataSeries.getAllX(), rVDataSeries.getAllY() );
1188 sal_Int32 nPointCount = 100 * fPointScale;
1190 if ( nPointCount < 2 )
1191 nPointCount = 2;
1193 std::vector< ExplicitScaleData > aScales( m_pPosHelper->getScales());
1194 uno::Reference< chart2::XScaling > xScalingX;
1195 uno::Reference< chart2::XScaling > xScalingY;
1196 if( aScales.size() >= 2 )
1198 xScalingX.set( aScales[0].Scaling );
1199 xScalingY.set( aScales[1].Scaling );
1202 uno::Sequence< geometry::RealPoint2D > aCalculatedPoints(
1203 xCalculator->getCurveValues(
1204 fMinX, fMaxX, nPointCount,
1205 xScalingX, xScalingY, bMaySkipPoints ));
1207 nPointCount = aCalculatedPoints.getLength();
1209 drawing::PolyPolygonShape3D aRegressionPoly;
1210 aRegressionPoly.SequenceX.realloc(1);
1211 aRegressionPoly.SequenceY.realloc(1);
1212 aRegressionPoly.SequenceZ.realloc(1);
1213 aRegressionPoly.SequenceX[0].realloc(nPointCount);
1214 aRegressionPoly.SequenceY[0].realloc(nPointCount);
1215 aRegressionPoly.SequenceZ[0].realloc(nPointCount);
1217 sal_Int32 nRealPointCount = 0;
1219 for(sal_Int32 nP = 0; nP < aCalculatedPoints.getLength(); ++nP)
1221 double fLogicX = aCalculatedPoints[nP].X;
1222 double fLogicY = aCalculatedPoints[nP].Y;
1223 double fLogicZ = 0.0; //dummy
1225 // fdo#51656: don't scale mean value lines
1226 if(!bAverageLine)
1227 m_pPosHelper->doLogicScaling( &fLogicX, &fLogicY, &fLogicZ );
1229 if(!rtl::math::isNan(fLogicX) && !rtl::math::isInf(fLogicX) &&
1230 !rtl::math::isNan(fLogicY) && !rtl::math::isInf(fLogicY) &&
1231 !rtl::math::isNan(fLogicZ) && !rtl::math::isInf(fLogicZ) )
1233 aRegressionPoly.SequenceX[0][nRealPointCount] = fLogicX;
1234 aRegressionPoly.SequenceY[0][nRealPointCount] = fLogicY;
1235 nRealPointCount++;
1238 aRegressionPoly.SequenceX[0].realloc(nRealPointCount);
1239 aRegressionPoly.SequenceY[0].realloc(nRealPointCount);
1240 aRegressionPoly.SequenceZ[0].realloc(nRealPointCount);
1242 drawing::PolyPolygonShape3D aClippedPoly;
1243 Clipping::clipPolygonAtRectangle( aRegressionPoly, m_pPosHelper->getScaledLogicClipDoubleRect(), aClippedPoly );
1244 aRegressionPoly = aClippedPoly;
1245 m_pPosHelper->transformScaledLogicToScene( aRegressionPoly );
1247 awt::Point aDefaultPos;
1248 if( aRegressionPoly.SequenceX.getLength() && aRegressionPoly.SequenceX[0].getLength() )
1250 VLineProperties aVLineProperties;
1251 aVLineProperties.initFromPropertySet( xProperties );
1253 //create an extra group shape for each curve for selection handling
1254 uno::Reference< drawing::XShapes > xRegressionGroupShapes =
1255 createGroupShape( xTarget, rVDataSeries.getDataCurveCID( nN, bAverageLine ) );
1256 uno::Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D(
1257 xRegressionGroupShapes, PolyToPointSequence( aRegressionPoly ), &aVLineProperties );
1258 AbstractShapeFactory::setShapeName( xShape, "MarkHandles" );
1259 aDefaultPos = xShape->getPosition();
1262 // curve equation and correlation coefficient
1263 uno::Reference< beans::XPropertySet > xEquationProperties( aCurveList[nN]->getEquationProperties());
1264 if( xEquationProperties.is())
1266 createRegressionCurveEquationShapes(
1267 rVDataSeries.getDataCurveEquationCID( nN ),
1268 xEquationProperties, xEquationTarget, xCalculator,
1269 aDefaultPos );
1274 sal_Int32 lcl_getOUStringMaxLineLength ( OUStringBuffer const & aString )
1276 const sal_Int32 nStringLength = aString.getLength();
1277 sal_Int32 nMaxLineLength = 0;
1279 for ( sal_Int32 i=0; i<nStringLength; i++ )
1281 sal_Int32 indexSep = aString.indexOf( "\n", i );
1282 if ( indexSep < 0 )
1283 indexSep = nStringLength;
1284 sal_Int32 nLineLength = indexSep - i;
1285 if ( nLineLength > nMaxLineLength )
1286 nMaxLineLength = nLineLength;
1287 i = indexSep;
1290 return nMaxLineLength;
1293 void VSeriesPlotter::createRegressionCurveEquationShapes(
1294 const OUString & rEquationCID,
1295 const uno::Reference< beans::XPropertySet > & xEquationProperties,
1296 const uno::Reference< drawing::XShapes >& xEquationTarget,
1297 const uno::Reference< chart2::XRegressionCurveCalculator > & xRegressionCurveCalculator,
1298 awt::Point aDefaultPos )
1300 OSL_ASSERT( xEquationProperties.is());
1301 if( !xEquationProperties.is())
1302 return;
1304 bool bShowEquation = false;
1305 bool bShowCorrCoeff = false;
1306 if(( xEquationProperties->getPropertyValue( "ShowEquation") >>= bShowEquation ) &&
1307 ( xEquationProperties->getPropertyValue( "ShowCorrelationCoefficient") >>= bShowCorrCoeff ))
1309 if( ! (bShowEquation || bShowCorrCoeff))
1310 return;
1312 OUStringBuffer aFormula;
1313 sal_Int32 nNumberFormatKey = 0;
1314 sal_Int32 nFormulaWidth = 0;
1315 xEquationProperties->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormatKey;
1316 bool bResizeEquation = true;
1317 sal_Int32 nMaxIteration = 2;
1318 if ( bShowEquation )
1320 OUString aXName, aYName;
1321 if ( !(xEquationProperties->getPropertyValue( "XName" ) >>= aXName) )
1322 aXName = OUString( "x" );
1323 if ( !(xEquationProperties->getPropertyValue( "YName" ) >>= aYName) )
1324 aYName = OUString( "f(x)" );
1325 xRegressionCurveCalculator->setXYNames( aXName, aYName );
1328 for ( sal_Int32 nCountIteration = 0; bResizeEquation && nCountIteration < nMaxIteration ; nCountIteration++ )
1330 bResizeEquation = false;
1331 if( bShowEquation )
1333 if( m_apNumberFormatterWrapper.get())
1334 { // iteration 0: default representation (no wrap)
1335 // iteration 1: expected width (nFormulaWidth) is calculated
1336 aFormula = xRegressionCurveCalculator->getFormattedRepresentation(
1337 m_apNumberFormatterWrapper->getNumberFormatsSupplier(),
1338 nNumberFormatKey, nFormulaWidth );
1339 nFormulaWidth = lcl_getOUStringMaxLineLength( aFormula );
1341 else
1343 aFormula = xRegressionCurveCalculator->getRepresentation();
1346 if( bShowCorrCoeff )
1348 aFormula.append( "\n" );
1351 if( bShowCorrCoeff )
1353 aFormula.append( "R" + OUStringLiteral1( aSuperscriptFigures[2] ) + " = " );
1354 double fR( xRegressionCurveCalculator->getCorrelationCoefficient());
1355 if( m_apNumberFormatterWrapper.get())
1357 Color nLabelCol;
1358 bool bColChanged;
1359 aFormula.append(
1360 m_apNumberFormatterWrapper->getFormattedString(
1361 nNumberFormatKey, fR*fR, nLabelCol, bColChanged ));
1362 //@todo: change color of label if bColChanged is true
1364 else
1366 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
1367 const OUString& aNumDecimalSep = rLocaleDataWrapper.getNumDecimalSep();
1368 sal_Unicode aDecimalSep = aNumDecimalSep[0];
1369 aFormula.append( ::rtl::math::doubleToUString(
1370 fR*fR, rtl_math_StringFormat_G, 4, aDecimalSep, true ));
1374 awt::Point aScreenPosition2D;
1375 chart2::RelativePosition aRelativePosition;
1376 if( xEquationProperties->getPropertyValue( "RelativePosition") >>= aRelativePosition )
1378 //@todo decide whether x is primary or secondary
1379 double fX = aRelativePosition.Primary*m_aPageReferenceSize.Width;
1380 double fY = aRelativePosition.Secondary*m_aPageReferenceSize.Height;
1381 aScreenPosition2D.X = static_cast< sal_Int32 >( ::rtl::math::round( fX ));
1382 aScreenPosition2D.Y = static_cast< sal_Int32 >( ::rtl::math::round( fY ));
1384 else
1385 aScreenPosition2D = aDefaultPos;
1387 if( !aFormula.isEmpty())
1389 // set fill and line properties on creation
1390 tNameSequence aNames;
1391 tAnySequence aValues;
1392 PropertyMapper::getPreparedTextShapePropertyLists( xEquationProperties, aNames, aValues );
1394 uno::Reference< drawing::XShape > xTextShape = m_pShapeFactory->createText(
1395 xEquationTarget, aFormula.makeStringAndClear(),
1396 aNames, aValues, AbstractShapeFactory::makeTransformation( aScreenPosition2D ));
1398 OSL_ASSERT( xTextShape.is());
1399 if( xTextShape.is())
1401 AbstractShapeFactory::setShapeName( xTextShape, rEquationCID );
1402 awt::Size aSize( xTextShape->getSize() );
1403 awt::Point aPos( RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
1404 aScreenPosition2D, aSize, aRelativePosition.Anchor ) );
1405 //ensure that the equation is fully placed within the page (if possible)
1406 if( (aPos.X + aSize.Width) > m_aPageReferenceSize.Width )
1407 aPos.X = m_aPageReferenceSize.Width - aSize.Width;
1408 if( aPos.X < 0 )
1410 aPos.X = 0;
1411 if ( nFormulaWidth > 0 )
1413 bResizeEquation = true;
1414 if ( nCountIteration < nMaxIteration-1 )
1415 xEquationTarget->remove( xTextShape ); // remove equation
1416 nFormulaWidth *= m_aPageReferenceSize.Width / static_cast< double >(aSize.Width);
1417 nFormulaWidth -= nCountIteration;
1418 if ( nFormulaWidth < 0 )
1419 nFormulaWidth = 0;
1422 if( (aPos.Y + aSize.Height) > m_aPageReferenceSize.Height )
1423 aPos.Y = m_aPageReferenceSize.Height - aSize.Height;
1424 if( aPos.Y < 0 )
1425 aPos.Y = 0;
1426 if ( !bResizeEquation || nCountIteration == nMaxIteration-1 )
1427 xTextShape->setPosition(aPos); // if equation was not removed
1434 void VSeriesPlotter::setMappedProperties(
1435 const uno::Reference< drawing::XShape >& xTargetShape
1436 , const uno::Reference< beans::XPropertySet >& xSource
1437 , const tPropertyNameMap& rMap
1438 , tPropertyNameValueMap const * pOverwriteMap )
1440 uno::Reference< beans::XPropertySet > xTargetProp( xTargetShape, uno::UNO_QUERY );
1441 PropertyMapper::setMappedProperties(xTargetProp,xSource,rMap,pOverwriteMap);
1444 void VSeriesPlotter::setTimeResolutionOnXAxis( long TimeResolution, const Date& rNullDate )
1446 m_nTimeResolution = TimeResolution;
1447 m_aNullDate = rNullDate;
1450 // MinimumAndMaximumSupplier
1451 long VSeriesPlotter::calculateTimeResolutionOnXAxis()
1453 long nRet = css::chart::TimeUnit::YEAR;
1454 if( m_pExplicitCategoriesProvider )
1456 const std::vector< double >& rDateCategories = m_pExplicitCategoriesProvider->getDateCategories();
1457 Date aNullDate(30,12,1899);
1458 if( m_apNumberFormatterWrapper.get() )
1459 aNullDate = m_apNumberFormatterWrapper->getNullDate();
1460 if( !rDateCategories.empty() )
1462 std::vector< double >::const_iterator aIt = rDateCategories.begin(), aEnd = rDateCategories.end();
1463 Date aPrevious(aNullDate); aPrevious.AddDays(rtl::math::approxFloor(*aIt));
1464 ++aIt;
1465 for(;aIt!=aEnd;++aIt)
1467 Date aCurrent(aNullDate); aCurrent.AddDays(rtl::math::approxFloor(*aIt));
1468 if( nRet == css::chart::TimeUnit::YEAR )
1470 if( DateHelper::IsInSameYear( aPrevious, aCurrent ) )
1471 nRet = css::chart::TimeUnit::MONTH;
1473 if( nRet == css::chart::TimeUnit::MONTH )
1475 if( DateHelper::IsInSameMonth( aPrevious, aCurrent ) )
1476 nRet = css::chart::TimeUnit::DAY;
1478 if( nRet == css::chart::TimeUnit::DAY )
1479 break;
1480 aPrevious=aCurrent;
1484 return nRet;
1486 double VSeriesPlotter::getMinimumX()
1488 double fMinimum, fMaximum;
1489 getMinimumAndMaximiumX( fMinimum, fMaximum );
1490 return fMinimum;
1492 double VSeriesPlotter::getMaximumX()
1494 double fMinimum, fMaximum;
1495 getMinimumAndMaximiumX( fMinimum, fMaximum );
1496 return fMaximum;
1499 double VSeriesPlotter::getMinimumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex )
1501 if( !m_bCategoryXAxis || ( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() ) )
1503 double fMinY, fMaxY;
1504 getMinimumAndMaximiumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex );
1505 return fMinY;
1508 double fMinimum, fMaximum;
1509 ::rtl::math::setInf(&fMinimum, false);
1510 ::rtl::math::setInf(&fMaximum, true);
1511 for(std::vector<VDataSeriesGroup> & rXSlots : m_aZSlots)
1513 for(VDataSeriesGroup & rXSlot : rXSlots)
1515 double fLocalMinimum, fLocalMaximum;
1516 rXSlot.calculateYMinAndMaxForCategoryRange(
1517 static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0
1518 , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0
1519 , isSeparateStackingForDifferentSigns( 1 )
1520 , fLocalMinimum, fLocalMaximum, nAxisIndex );
1521 if(fMaximum<fLocalMaximum)
1522 fMaximum=fLocalMaximum;
1523 if(fMinimum>fLocalMinimum)
1524 fMinimum=fLocalMinimum;
1527 if(::rtl::math::isInf(fMinimum))
1528 ::rtl::math::setNan(&fMinimum);
1529 return fMinimum;
1532 double VSeriesPlotter::getMaximumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex )
1534 if( !m_bCategoryXAxis || ( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() ) )
1536 double fMinY, fMaxY;
1537 getMinimumAndMaximiumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex );
1538 return fMaxY;
1541 double fMinimum, fMaximum;
1542 ::rtl::math::setInf(&fMinimum, false);
1543 ::rtl::math::setInf(&fMaximum, true);
1544 for( std::vector< VDataSeriesGroup > & rXSlots : m_aZSlots)
1546 for(VDataSeriesGroup & rXSlot : rXSlots)
1548 double fLocalMinimum, fLocalMaximum;
1549 rXSlot.calculateYMinAndMaxForCategoryRange(
1550 static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0
1551 , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0
1552 , isSeparateStackingForDifferentSigns( 1 )
1553 , fLocalMinimum, fLocalMaximum, nAxisIndex );
1554 if(fMaximum<fLocalMaximum)
1555 fMaximum=fLocalMaximum;
1556 if(fMinimum>fLocalMinimum)
1557 fMinimum=fLocalMinimum;
1560 if(::rtl::math::isInf(fMaximum))
1561 ::rtl::math::setNan(&fMaximum);
1562 return fMaximum;
1565 double VSeriesPlotter::getMinimumZ()
1567 //this is the default for all charts without a meaningful z axis
1568 return 1.0;
1570 double VSeriesPlotter::getMaximumZ()
1572 if( m_nDimension!=3 || !m_aZSlots.size() )
1573 return getMinimumZ()+1;
1574 return m_aZSlots.size();
1577 namespace
1579 bool lcl_isValueAxis( sal_Int32 nDimensionIndex, bool bCategoryXAxis )
1581 // default implementation: true for Y axes, and for value X axis
1582 if( nDimensionIndex == 0 )
1583 return !bCategoryXAxis;
1584 return nDimensionIndex == 1;
1588 bool VSeriesPlotter::isExpandBorderToIncrementRhythm( sal_Int32 nDimensionIndex )
1590 return lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis );
1593 bool VSeriesPlotter::isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex )
1595 // do not expand axes in 3D charts
1596 return (m_nDimension < 3) && lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis );
1599 bool VSeriesPlotter::isExpandWideValuesToZero( sal_Int32 nDimensionIndex )
1601 // default implementation: only for Y axis
1602 return nDimensionIndex == 1;
1605 bool VSeriesPlotter::isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex )
1607 // default implementation: only for Y axis
1608 return nDimensionIndex == 1;
1611 bool VSeriesPlotter::isSeparateStackingForDifferentSigns( sal_Int32 nDimensionIndex )
1613 // default implementation: only for Y axis
1614 return nDimensionIndex == 1;
1617 void VSeriesPlotter::getMinimumAndMaximiumX( double& rfMinimum, double& rfMaximum ) const
1619 ::rtl::math::setInf(&rfMinimum, false);
1620 ::rtl::math::setInf(&rfMaximum, true);
1622 for (auto const& ZSlot : m_aZSlots)
1624 for (auto const& XSlot : ZSlot)
1626 double fLocalMinimum, fLocalMaximum;
1627 XSlot.getMinimumAndMaximiumX( fLocalMinimum, fLocalMaximum );
1628 if( !::rtl::math::isNan(fLocalMinimum) && fLocalMinimum< rfMinimum )
1629 rfMinimum = fLocalMinimum;
1630 if( !::rtl::math::isNan(fLocalMaximum) && fLocalMaximum> rfMaximum )
1631 rfMaximum = fLocalMaximum;
1634 if(::rtl::math::isInf(rfMinimum))
1635 ::rtl::math::setNan(&rfMinimum);
1636 if(::rtl::math::isInf(rfMaximum))
1637 ::rtl::math::setNan(&rfMaximum);
1640 void VSeriesPlotter::getMinimumAndMaximiumYInContinuousXRange( double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const
1642 ::rtl::math::setInf(&rfMinY, false);
1643 ::rtl::math::setInf(&rfMaxY, true);
1645 for (auto const& ZSlot : m_aZSlots)
1647 for (auto const& XSlot : ZSlot)
1649 double fLocalMinimum, fLocalMaximum;
1650 XSlot.getMinimumAndMaximiumYInContinuousXRange( fLocalMinimum, fLocalMaximum, fMinX, fMaxX, nAxisIndex );
1651 if( !::rtl::math::isNan(fLocalMinimum) && fLocalMinimum< rfMinY )
1652 rfMinY = fLocalMinimum;
1653 if( !::rtl::math::isNan(fLocalMaximum) && fLocalMaximum> rfMaxY )
1654 rfMaxY = fLocalMaximum;
1657 if(::rtl::math::isInf(rfMinY))
1658 ::rtl::math::setNan(&rfMinY);
1659 if(::rtl::math::isInf(rfMaxY))
1660 ::rtl::math::setNan(&rfMaxY);
1663 sal_Int32 VSeriesPlotter::getPointCount() const
1665 sal_Int32 nRet = 0;
1667 for (auto const& ZSlot : m_aZSlots)
1669 for (auto const& XSlot : ZSlot)
1671 sal_Int32 nPointCount = XSlot.getPointCount();
1672 if( nPointCount>nRet )
1673 nRet = nPointCount;
1676 return nRet;
1679 void VSeriesPlotter::setNumberFormatsSupplier(
1680 const uno::Reference< util::XNumberFormatsSupplier > & xNumFmtSupplier )
1682 m_apNumberFormatterWrapper.reset( new NumberFormatterWrapper( xNumFmtSupplier ));
1685 void VSeriesPlotter::setColorScheme( const uno::Reference< XColorScheme >& xColorScheme )
1687 m_xColorScheme = xColorScheme;
1690 void VSeriesPlotter::setExplicitCategoriesProvider( ExplicitCategoriesProvider* pExplicitCategoriesProvider )
1692 m_pExplicitCategoriesProvider = pExplicitCategoriesProvider;
1695 sal_Int32 VDataSeriesGroup::getPointCount() const
1697 if(!m_bMaxPointCountDirty)
1698 return m_nMaxPointCount;
1700 sal_Int32 nRet = 0;
1702 for (VDataSeries* pSeries : m_aSeriesVector)
1704 sal_Int32 nPointCount = pSeries->getTotalPointCount();
1705 if( nPointCount>nRet )
1706 nRet = nPointCount;
1708 m_nMaxPointCount=nRet;
1709 m_aListOfCachedYValues.clear();
1710 m_aListOfCachedYValues.resize(m_nMaxPointCount);
1711 m_bMaxPointCountDirty=false;
1712 return nRet;
1715 sal_Int32 VDataSeriesGroup::getAttachedAxisIndexForFirstSeries() const
1717 sal_Int32 nRet = 0;
1719 if (!m_aSeriesVector.empty())
1720 nRet = m_aSeriesVector[0]->getAttachedAxisIndex();
1722 return nRet;
1725 void VDataSeriesGroup::getMinimumAndMaximiumX( double& rfMinimum, double& rfMaximum ) const
1728 ::rtl::math::setInf(&rfMinimum, false);
1729 ::rtl::math::setInf(&rfMaximum, true);
1731 for (VDataSeries* pSeries : m_aSeriesVector)
1733 sal_Int32 nPointCount = pSeries->getTotalPointCount();
1734 for(sal_Int32 nN=0;nN<nPointCount;nN++)
1736 double fX = pSeries->getXValue( nN );
1737 if( ::rtl::math::isNan(fX) )
1738 continue;
1739 if(rfMaximum<fX)
1740 rfMaximum=fX;
1741 if(rfMinimum>fX)
1742 rfMinimum=fX;
1745 if(::rtl::math::isInf(rfMinimum))
1746 ::rtl::math::setNan(&rfMinimum);
1747 if(::rtl::math::isInf(rfMaximum))
1748 ::rtl::math::setNan(&rfMaximum);
1751 namespace {
1754 * Keep track of minimum and maximum Y values for one or more data series.
1755 * When multiple data series exist, that indicates that the data series are
1756 * stacked.
1758 * <p>For each X value, we calculate separate Y value ranges for each data
1759 * series in the first pass. In the second pass, we calculate the minimum Y
1760 * value by taking the absolute minimum value of all data series, whereas
1761 * the maximum Y value is the sum of all the series maximum Y values.</p>
1763 * <p>Once that's done for all X values, the final min / max Y values get
1764 * calculated by taking the absolute min / max Y values across all the X
1765 * values.</p>
1767 class PerXMinMaxCalculator
1769 typedef std::pair<double, double> MinMaxType;
1770 typedef std::map<size_t, MinMaxType> SeriesMinMaxType;
1771 typedef std::map<double, std::unique_ptr<SeriesMinMaxType>> GroupMinMaxType;
1772 typedef std::unordered_map<double, MinMaxType> TotalStoreType;
1773 GroupMinMaxType m_SeriesGroup;
1774 size_t mnCurSeries;
1776 public:
1777 PerXMinMaxCalculator() : mnCurSeries(0) {}
1779 void nextSeries() { ++mnCurSeries; }
1781 void setValue(double fX, double fY)
1783 SeriesMinMaxType* pStore = getByXValue(fX); // get storage for given X value.
1784 if (!pStore)
1785 // This shouldn't happen!
1786 return;
1788 SeriesMinMaxType::iterator it = pStore->lower_bound(mnCurSeries);
1789 if (it != pStore->end() && !pStore->key_comp()(mnCurSeries, it->first))
1791 MinMaxType& r = it->second;
1792 // A min-max pair already exists for this series. Update it.
1793 if (fY < r.first)
1794 r.first = fY;
1795 if (r.second < fY)
1796 r.second = fY;
1798 else
1800 // No existing pair. Insert a new one.
1801 pStore->insert(
1802 it, SeriesMinMaxType::value_type(
1803 mnCurSeries, MinMaxType(fY,fY)));
1807 void getTotalRange(double& rfMin, double& rfMax) const
1809 rtl::math::setNan(&rfMin);
1810 rtl::math::setNan(&rfMax);
1812 TotalStoreType aStore;
1813 getTotalStore(aStore);
1815 if (aStore.empty())
1816 return;
1818 TotalStoreType::const_iterator it = aStore.begin(), itEnd = aStore.end();
1819 rfMin = it->second.first;
1820 rfMax = it->second.second;
1821 for (++it; it != itEnd; ++it)
1823 if (rfMin > it->second.first)
1824 rfMin = it->second.first;
1825 if (rfMax < it->second.second)
1826 rfMax = it->second.second;
1830 private:
1832 * Parse all data and reduce them into a set of global Y value ranges per
1833 * X value.
1835 void getTotalStore(TotalStoreType& rStore) const
1837 TotalStoreType aStore;
1838 for (auto const& it : m_SeriesGroup)
1840 double fX = it.first;
1842 const SeriesMinMaxType& rSeries = *it.second;
1843 for (auto const& series : rSeries)
1845 double fYMin = series.second.first, fYMax = series.second.second;
1846 TotalStoreType::iterator itr = aStore.find(fX);
1847 if (itr == aStore.end())
1848 // New min-max pair for give X value.
1849 aStore.emplace(fX, std::pair<double,double>(fYMin,fYMax));
1850 else
1852 MinMaxType& r = itr->second;
1853 if (fYMin < r.first)
1854 r.first = fYMin; // min y-value
1856 r.second += fYMax; // accumulative max y-value.
1860 rStore.swap(aStore);
1863 SeriesMinMaxType* getByXValue(double fX)
1865 GroupMinMaxType::iterator it = m_SeriesGroup.find(fX);
1866 if (it == m_SeriesGroup.end())
1868 std::pair<GroupMinMaxType::iterator,bool> r =
1869 m_SeriesGroup.insert(std::make_pair(fX, o3tl::make_unique<SeriesMinMaxType>()));
1871 if (!r.second)
1872 // insertion failed.
1873 return nullptr;
1875 it = r.first;
1878 return it->second.get();
1884 void VDataSeriesGroup::getMinimumAndMaximiumYInContinuousXRange(
1885 double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const
1887 ::rtl::math::setNan(&rfMinY);
1888 ::rtl::math::setNan(&rfMaxY);
1890 if (m_aSeriesVector.empty())
1891 // No data series. Bail out.
1892 return;
1894 PerXMinMaxCalculator aRangeCalc;
1895 for (const VDataSeries* pSeries : m_aSeriesVector)
1897 if (!pSeries)
1898 continue;
1900 for (sal_Int32 i = 0, n = pSeries->getTotalPointCount(); i < n; ++i)
1902 if (nAxisIndex != pSeries->getAttachedAxisIndex())
1903 continue;
1905 double fX = pSeries->getXValue(i);
1906 if (rtl::math::isNan(fX))
1907 continue;
1909 if (fX < fMinX || fX > fMaxX)
1910 // Outside specified X range. Skip it.
1911 continue;
1913 double fY = pSeries->getYValue(i);
1914 if (::rtl::math::isNan(fY))
1915 continue;
1917 aRangeCalc.setValue(fX, fY);
1919 aRangeCalc.nextSeries();
1922 aRangeCalc.getTotalRange(rfMinY, rfMaxY);
1925 void VDataSeriesGroup::calculateYMinAndMaxForCategory( sal_Int32 nCategoryIndex
1926 , bool bSeparateStackingForDifferentSigns
1927 , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex )
1929 assert(nCategoryIndex >= 0);
1930 assert(nCategoryIndex < getPointCount());
1932 ::rtl::math::setInf(&rfMinimumY, false);
1933 ::rtl::math::setInf(&rfMaximumY, true);
1935 if(m_aSeriesVector.empty())
1936 return;
1938 CachedYValues aCachedYValues = m_aListOfCachedYValues[nCategoryIndex][nAxisIndex];
1939 if( !aCachedYValues.m_bValuesDirty )
1941 //return cached values
1942 rfMinimumY = aCachedYValues.m_fMinimumY;
1943 rfMaximumY = aCachedYValues.m_fMaximumY;
1944 return;
1947 double fTotalSum, fPositiveSum, fNegativeSum, fFirstPositiveY, fFirstNegativeY;
1948 ::rtl::math::setNan( &fTotalSum );
1949 ::rtl::math::setNan( &fPositiveSum );
1950 ::rtl::math::setNan( &fNegativeSum );
1951 ::rtl::math::setNan( &fFirstPositiveY );
1952 ::rtl::math::setNan( &fFirstNegativeY );
1954 if( bSeparateStackingForDifferentSigns )
1956 for (const VDataSeries* pSeries: m_aSeriesVector)
1958 if( nAxisIndex != pSeries->getAttachedAxisIndex() )
1959 continue;
1961 double fValueMinY = pSeries->getMinimumofAllDifferentYValues( nCategoryIndex );
1962 double fValueMaxY = pSeries->getMaximumofAllDifferentYValues( nCategoryIndex );
1964 if( fValueMaxY >= 0 )
1966 if( ::rtl::math::isNan( fPositiveSum ) )
1967 fPositiveSum = fFirstPositiveY = fValueMaxY;
1968 else
1969 fPositiveSum += fValueMaxY;
1971 if( fValueMinY < 0 )
1973 if(::rtl::math::isNan( fNegativeSum ))
1974 fNegativeSum = fFirstNegativeY = fValueMinY;
1975 else
1976 fNegativeSum += fValueMinY;
1979 rfMinimumY = ::rtl::math::isNan( fNegativeSum ) ? fFirstPositiveY : fNegativeSum;
1980 rfMaximumY = ::rtl::math::isNan( fPositiveSum ) ? fFirstNegativeY : fPositiveSum;
1982 else
1984 for (const VDataSeries* pSeries: m_aSeriesVector)
1986 if( nAxisIndex != pSeries->getAttachedAxisIndex() )
1987 continue;
1989 double fValueMinY = pSeries->getMinimumofAllDifferentYValues( nCategoryIndex );
1990 double fValueMaxY = pSeries->getMaximumofAllDifferentYValues( nCategoryIndex );
1992 if( ::rtl::math::isNan( fTotalSum ) )
1994 rfMinimumY = fValueMinY;
1995 rfMaximumY = fTotalSum = fValueMaxY;
1997 else
1999 fTotalSum += fValueMaxY;
2000 if( rfMinimumY > fTotalSum )
2001 rfMinimumY = fTotalSum;
2002 if( rfMaximumY < fTotalSum )
2003 rfMaximumY = fTotalSum;
2008 aCachedYValues.m_fMinimumY = rfMinimumY;
2009 aCachedYValues.m_fMaximumY = rfMaximumY;
2010 aCachedYValues.m_bValuesDirty = false;
2011 m_aListOfCachedYValues[nCategoryIndex][nAxisIndex]=aCachedYValues;
2014 void VDataSeriesGroup::calculateYMinAndMaxForCategoryRange(
2015 sal_Int32 nStartCategoryIndex, sal_Int32 nEndCategoryIndex
2016 , bool bSeparateStackingForDifferentSigns
2017 , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex )
2019 //@todo maybe cache these values
2020 ::rtl::math::setInf(&rfMinimumY, false);
2021 ::rtl::math::setInf(&rfMaximumY, true);
2023 //iterate through the given categories
2024 if(nStartCategoryIndex<0)
2025 nStartCategoryIndex=0;
2026 const sal_Int32 nPointCount = getPointCount();//necessary to create m_aListOfCachedYValues
2027 if(nPointCount <= 0)
2028 return;
2029 if (nEndCategoryIndex >= nPointCount)
2030 nEndCategoryIndex = nPointCount - 1;
2031 if(nEndCategoryIndex<0)
2032 nEndCategoryIndex=0;
2033 for( sal_Int32 nCatIndex = nStartCategoryIndex; nCatIndex <= nEndCategoryIndex; nCatIndex++ )
2035 double fMinimumY; ::rtl::math::setNan(&fMinimumY);
2036 double fMaximumY; ::rtl::math::setNan(&fMaximumY);
2038 calculateYMinAndMaxForCategory( nCatIndex
2039 , bSeparateStackingForDifferentSigns, fMinimumY, fMaximumY, nAxisIndex );
2041 if(rfMinimumY > fMinimumY)
2042 rfMinimumY = fMinimumY;
2043 if(rfMaximumY < fMaximumY)
2044 rfMaximumY = fMaximumY;
2048 double VSeriesPlotter::getTransformedDepth() const
2050 double MinZ = m_pMainPosHelper->getLogicMinZ();
2051 double MaxZ = m_pMainPosHelper->getLogicMaxZ();
2052 m_pMainPosHelper->doLogicScaling( nullptr, nullptr, &MinZ );
2053 m_pMainPosHelper->doLogicScaling( nullptr, nullptr, &MaxZ );
2054 return FIXED_SIZE_FOR_3D_CHART_VOLUME/(MaxZ-MinZ);
2057 void VSeriesPlotter::addSecondaryValueScale( const ExplicitScaleData& rScale, sal_Int32 nAxisIndex )
2059 if( nAxisIndex<1 )
2060 return;
2062 m_aSecondaryValueScales[nAxisIndex]=rScale;
2065 PlottingPositionHelper& VSeriesPlotter::getPlottingPositionHelper( sal_Int32 nAxisIndex ) const
2067 PlottingPositionHelper* pRet = nullptr;
2068 if(nAxisIndex>0)
2070 tSecondaryPosHelperMap::const_iterator aPosIt = m_aSecondaryPosHelperMap.find( nAxisIndex );
2071 if( aPosIt != m_aSecondaryPosHelperMap.end() )
2073 pRet = aPosIt->second;
2075 else if (m_pPosHelper)
2077 tSecondaryValueScales::const_iterator aScaleIt = m_aSecondaryValueScales.find( nAxisIndex );
2078 if( aScaleIt != m_aSecondaryValueScales.end() )
2080 pRet = m_pPosHelper->createSecondaryPosHelper( aScaleIt->second );
2081 m_aSecondaryPosHelperMap[nAxisIndex] = pRet;
2085 if( !pRet )
2086 pRet = m_pMainPosHelper;
2087 if(pRet)
2088 pRet->setTimeResolution( m_nTimeResolution, m_aNullDate );
2089 return *pRet;
2092 void VSeriesPlotter::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& /*rPageSize*/ )
2096 VDataSeries* VSeriesPlotter::getFirstSeries() const
2098 for (std::vector<VDataSeriesGroup> const & rGroup : m_aZSlots)
2100 if (!rGroup.empty())
2102 if (!rGroup[0].m_aSeriesVector.empty())
2104 VDataSeries* pSeries = rGroup[0].m_aSeriesVector[0];
2105 if (pSeries)
2106 return pSeries;
2110 return nullptr;
2113 OUString VSeriesPlotter::getCategoryName( sal_Int32 nPointIndex ) const
2115 if (m_pExplicitCategoriesProvider)
2117 Sequence< OUString > aCategories(m_pExplicitCategoriesProvider->getSimpleCategories());
2118 if (nPointIndex >= 0 && nPointIndex < aCategories.getLength())
2120 return aCategories[nPointIndex];
2123 return OUString();
2126 uno::Sequence< OUString > VSeriesPlotter::getSeriesNames() const
2128 std::vector<OUString> aRetVector;
2130 OUString aRole;
2131 if( m_xChartTypeModel.is() )
2132 aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel();
2134 for (auto const& rGroup : m_aZSlots)
2136 if (!rGroup.empty())
2138 VDataSeriesGroup aSeriesGroup(rGroup[0]);
2139 if (!aSeriesGroup.m_aSeriesVector.empty())
2141 VDataSeries* pSeries = aSeriesGroup.m_aSeriesVector[0];
2142 uno::Reference< XDataSeries > xSeries( pSeries ? pSeries->getModel() : nullptr );
2143 if( xSeries.is() )
2145 OUString aSeriesName( DataSeriesHelper::getDataSeriesLabel( xSeries, aRole ) );
2146 aRetVector.push_back( aSeriesName );
2151 return comphelper::containerToSequence( aRetVector );
2154 void VSeriesPlotter::setPageReferenceSize( const css::awt::Size & rPageRefSize )
2156 m_aPageReferenceSize = rPageRefSize;
2158 // set reference size also at all data series
2160 std::vector<VDataSeriesGroup> aSeriesGroups(FlattenVector(m_aZSlots));
2161 for (VDataSeriesGroup const & rGroup : aSeriesGroups)
2163 for (VDataSeries* pSeries : rGroup.m_aSeriesVector)
2165 pSeries->setPageReferenceSize(m_aPageReferenceSize);
2170 //better performance for big data
2171 void VSeriesPlotter::setCoordinateSystemResolution( const Sequence< sal_Int32 >& rCoordinateSystemResolution )
2173 m_aCoordinateSystemResolution = rCoordinateSystemResolution;
2176 bool VSeriesPlotter::WantToPlotInFrontOfAxisLine()
2178 return ChartTypeHelper::isSeriesInFrontOfAxisLine( m_xChartTypeModel );
2181 bool VSeriesPlotter::shouldSnapRectToUsedArea()
2183 return m_nDimension != 3;
2186 std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntries(
2187 const awt::Size& rEntryKeyAspectRatio
2188 , css::chart::ChartLegendExpansion eLegendExpansion
2189 , const Reference< beans::XPropertySet >& xTextProperties
2190 , const Reference< drawing::XShapes >& xTarget
2191 , const Reference< lang::XMultiServiceFactory >& xShapeFactory
2192 , const Reference< uno::XComponentContext >& xContext
2195 std::vector< ViewLegendEntry > aResult;
2197 if( xTarget.is() )
2199 //iterate through all series
2200 bool bBreak = false;
2201 bool bFirstSeries = true;
2204 for (std::vector<VDataSeriesGroup> const & rGroupVector : m_aZSlots)
2206 for (VDataSeriesGroup const & rGroup : rGroupVector)
2208 for (VDataSeries* pSeries : rGroup.m_aSeriesVector)
2210 if (!pSeries)
2211 continue;
2213 std::vector<ViewLegendEntry> aSeriesEntries(
2214 createLegendEntriesForSeries(
2215 rEntryKeyAspectRatio, *pSeries, xTextProperties,
2216 xTarget, xShapeFactory, xContext));
2218 //add series entries to the result now
2220 // use only the first series if VaryColorsByPoint is set for the first series
2221 if (bFirstSeries && pSeries->isVaryColorsByPoint())
2222 bBreak = true;
2223 bFirstSeries = false;
2225 // add entries reverse if chart is stacked in y-direction and the legend is not wide.
2226 // If the legend is wide and we have a stacked bar-chart the normal order
2227 // is the correct one
2228 bool bReverse = false;
2229 if( eLegendExpansion != css::chart::ChartLegendExpansion_WIDE )
2231 StackingDirection eStackingDirection( pSeries->getStackingDirection() );
2232 bReverse = ( eStackingDirection == StackingDirection_Y_STACKING );
2234 //todo: respect direction of axis in future
2237 if (bReverse)
2238 aResult.insert( aResult.begin(), aSeriesEntries.begin(), aSeriesEntries.end() );
2239 else
2240 aResult.insert( aResult.end(), aSeriesEntries.begin(), aSeriesEntries.end() );
2242 if (bBreak)
2243 return aResult;
2248 return aResult;
2251 std::vector<VDataSeries*> VSeriesPlotter::getAllSeries()
2253 std::vector<VDataSeries*> aAllSeries;
2254 for (std::vector<VDataSeriesGroup> const & rXSlot : m_aZSlots)
2256 for(VDataSeriesGroup const & rGroup : rXSlot)
2258 std::vector<VDataSeries*> aSeriesList = rGroup.m_aSeriesVector;
2259 aAllSeries.insert(aAllSeries.end(), aSeriesList.begin(), aSeriesList.end());
2262 return aAllSeries;
2265 namespace
2267 bool lcl_HasVisibleLine( const uno::Reference< beans::XPropertySet >& xProps, bool& rbHasDashedLine )
2269 bool bHasVisibleLine = false;
2270 rbHasDashedLine = false;
2271 drawing::LineStyle aLineStyle = drawing::LineStyle_NONE;
2272 if( xProps.is() && ( xProps->getPropertyValue( "LineStyle") >>= aLineStyle ) )
2274 if( aLineStyle != drawing::LineStyle_NONE )
2275 bHasVisibleLine = true;
2276 if( aLineStyle == drawing::LineStyle_DASH )
2277 rbHasDashedLine = true;
2279 return bHasVisibleLine;
2282 bool lcl_HasRegressionCurves( const VDataSeries& rSeries, bool& rbHasDashedLine )
2284 bool bHasRegressionCurves = false;
2285 Reference< XRegressionCurveContainer > xRegrCont( rSeries.getModel(), uno::UNO_QUERY );
2286 if( xRegrCont.is())
2288 Sequence< Reference< XRegressionCurve > > aCurves( xRegrCont->getRegressionCurves() );
2289 sal_Int32 i = 0, nCount = aCurves.getLength();
2290 for( i=0; i<nCount; ++i )
2292 if( aCurves[i].is() )
2294 bHasRegressionCurves = true;
2295 lcl_HasVisibleLine( uno::Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ), rbHasDashedLine );
2299 return bHasRegressionCurves;
2302 LegendSymbolStyle VSeriesPlotter::getLegendSymbolStyle()
2304 return LegendSymbolStyle_BOX;
2307 awt::Size VSeriesPlotter::getPreferredLegendKeyAspectRatio()
2309 awt::Size aRet(1000,1000);
2310 if( m_nDimension==3 )
2311 return aRet;
2313 bool bSeriesAllowsLines = (getLegendSymbolStyle() == LegendSymbolStyle_LINE);
2314 bool bHasLines = false;
2315 bool bHasDashedLines = false;
2316 //iterate through all series
2317 for (VDataSeries* pSeries : getAllSeries())
2319 if( bSeriesAllowsLines )
2321 bool bCurrentDashed = false;
2322 if( lcl_HasVisibleLine( pSeries->getPropertiesOfSeries(), bCurrentDashed ) )
2324 bHasLines = true;
2325 if( bCurrentDashed )
2327 bHasDashedLines = true;
2328 break;
2332 bool bRegressionHasDashedLines=false;
2333 if( lcl_HasRegressionCurves( *pSeries, bRegressionHasDashedLines ) )
2335 bHasLines = true;
2336 if( bRegressionHasDashedLines )
2338 bHasDashedLines = true;
2339 break;
2343 if( bHasLines )
2345 if( bHasDashedLines )
2346 aRet = awt::Size(1600,-1);
2347 else
2348 aRet = awt::Size(800,-1);
2350 return aRet;
2353 uno::Any VSeriesPlotter::getExplicitSymbol( const VDataSeries& /*rSeries*/, sal_Int32 /*nPointIndex*/ )
2355 return uno::Any();
2358 Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForSeries(
2359 const awt::Size& rEntryKeyAspectRatio
2360 , const VDataSeries& rSeries
2361 , const Reference< drawing::XShapes >& xTarget
2362 , const Reference< lang::XMultiServiceFactory >& xShapeFactory )
2365 LegendSymbolStyle eLegendSymbolStyle = getLegendSymbolStyle();
2366 uno::Any aExplicitSymbol( getExplicitSymbol( rSeries, -1 ) );
2368 VLegendSymbolFactory::PropertyType ePropType =
2369 VLegendSymbolFactory::PropertyType::FilledSeries;
2371 // todo: maybe the property-style does not solely depend on the
2372 // legend-symbol type
2373 switch( eLegendSymbolStyle )
2375 case LegendSymbolStyle_LINE:
2376 ePropType = VLegendSymbolFactory::PropertyType::LineSeries;
2377 break;
2378 default:
2379 break;
2381 Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio,
2382 xTarget, eLegendSymbolStyle, xShapeFactory
2383 , rSeries.getPropertiesOfSeries(), ePropType, aExplicitSymbol ));
2385 return xShape;
2388 Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForPoint(
2389 const awt::Size& rEntryKeyAspectRatio
2390 , const VDataSeries& rSeries
2391 , sal_Int32 nPointIndex
2392 , const Reference< drawing::XShapes >& xTarget
2393 , const Reference< lang::XMultiServiceFactory >& xShapeFactory )
2396 LegendSymbolStyle eLegendSymbolStyle = getLegendSymbolStyle();
2397 uno::Any aExplicitSymbol( getExplicitSymbol(rSeries,nPointIndex) );
2399 VLegendSymbolFactory::PropertyType ePropType =
2400 VLegendSymbolFactory::PropertyType::FilledSeries;
2402 // todo: maybe the property-style does not solely depend on the
2403 // legend-symbol type
2404 switch( eLegendSymbolStyle )
2406 case LegendSymbolStyle_LINE:
2407 ePropType = VLegendSymbolFactory::PropertyType::LineSeries;
2408 break;
2409 default:
2410 break;
2413 // the default properties for the data point are the data series properties.
2414 // If a data point has own attributes overwrite them
2415 Reference< beans::XPropertySet > xSeriesProps( rSeries.getPropertiesOfSeries() );
2416 Reference< beans::XPropertySet > xPointSet( xSeriesProps );
2417 if( rSeries.isAttributedDataPoint( nPointIndex ) )
2418 xPointSet.set( rSeries.getPropertiesOfPoint( nPointIndex ));
2420 // if a data point has no own color use a color fom the diagram's color scheme
2421 if( ! rSeries.hasPointOwnColor( nPointIndex ))
2423 Reference< util::XCloneable > xCloneable( xPointSet,uno::UNO_QUERY );
2424 if( xCloneable.is() && m_xColorScheme.is() )
2426 xPointSet.set( xCloneable->createClone(), uno::UNO_QUERY );
2427 Reference< container::XChild > xChild( xPointSet, uno::UNO_QUERY );
2428 if( xChild.is())
2429 xChild->setParent( xSeriesProps );
2431 OSL_ASSERT( xPointSet.is());
2432 xPointSet->setPropertyValue(
2433 "Color", uno::Any( m_xColorScheme->getColorByIndex( nPointIndex )));
2437 Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio,
2438 xTarget, eLegendSymbolStyle, xShapeFactory, xPointSet, ePropType, aExplicitSymbol ));
2440 return xShape;
2443 std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries(
2444 const awt::Size& rEntryKeyAspectRatio
2445 , const VDataSeries& rSeries
2446 , const Reference< beans::XPropertySet >& xTextProperties
2447 , const Reference< drawing::XShapes >& xTarget
2448 , const Reference< lang::XMultiServiceFactory >& xShapeFactory
2449 , const Reference< uno::XComponentContext >& xContext
2452 std::vector< ViewLegendEntry > aResult;
2454 if( ! ( xShapeFactory.is() && xTarget.is() && xContext.is() ) )
2455 return aResult;
2459 ViewLegendEntry aEntry;
2460 OUString aLabelText;
2461 bool bVaryColorsByPoint = rSeries.isVaryColorsByPoint();
2462 if( bVaryColorsByPoint )
2464 Sequence< OUString > aCategoryNames;
2465 if( m_pExplicitCategoriesProvider )
2466 aCategoryNames = m_pExplicitCategoriesProvider->getSimpleCategories();
2468 for( sal_Int32 nIdx=0; nIdx<aCategoryNames.getLength(); ++nIdx )
2470 // symbol
2471 uno::Reference< drawing::XShapes > xSymbolGroup( AbstractShapeFactory::getOrCreateShapeFactory(xShapeFactory)->createGroup2D( xTarget ));
2473 // create the symbol
2474 Reference< drawing::XShape > xShape( createLegendSymbolForPoint( rEntryKeyAspectRatio,
2475 rSeries, nIdx, xSymbolGroup, xShapeFactory ) );
2477 // set CID to symbol for selection
2478 if( xShape.is() )
2480 aEntry.aSymbol.set( xSymbolGroup, uno::UNO_QUERY );
2482 OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_DATA_POINT, nIdx ) );
2483 aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) );
2484 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2485 AbstractShapeFactory::setShapeName( xShape, aCID );
2488 // label
2489 aLabelText = aCategoryNames[nIdx];
2490 if( xShape.is() || !aLabelText.isEmpty() )
2492 aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties );
2493 aResult.push_back(aEntry);
2497 else
2499 // symbol
2500 uno::Reference< drawing::XShapes > xSymbolGroup( AbstractShapeFactory::getOrCreateShapeFactory(xShapeFactory)->createGroup2D( xTarget ));
2502 // create the symbol
2503 Reference< drawing::XShape > xShape( createLegendSymbolForSeries(
2504 rEntryKeyAspectRatio, rSeries, xSymbolGroup, xShapeFactory ) );
2506 // set CID to symbol for selection
2507 if( xShape.is())
2509 aEntry.aSymbol.set( xSymbolGroup, uno::UNO_QUERY );
2511 OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) );
2512 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2513 AbstractShapeFactory::setShapeName( xShape, aCID );
2516 // label
2517 aLabelText = DataSeriesHelper::getDataSeriesLabel( rSeries.getModel(), m_xChartTypeModel.is() ? m_xChartTypeModel->getRoleOfSequenceForSeriesLabel() : "values-y");
2518 aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties );
2520 aResult.push_back(aEntry);
2523 // don't show legend entry of regression curve & friends if this type of chart
2524 // doesn't support statistics #i63016#, fdo#37197
2525 if (!ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel, m_nDimension ))
2526 return aResult;
2528 Reference< XRegressionCurveContainer > xRegrCont( rSeries.getModel(), uno::UNO_QUERY );
2529 if( xRegrCont.is())
2531 Sequence< Reference< XRegressionCurve > > aCurves( xRegrCont->getRegressionCurves());
2532 sal_Int32 i = 0, nCount = aCurves.getLength();
2533 for( i=0; i<nCount; ++i )
2535 if( aCurves[i].is() )
2537 //label
2538 OUString aResStr( RegressionCurveHelper::getUINameForRegressionCurve( aCurves[i] ) );
2539 replaceParamterInString( aResStr, "%SERIESNAME", aLabelText );
2540 aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aResStr, xTextProperties );
2542 // symbol
2543 uno::Reference< drawing::XShapes > xSymbolGroup( AbstractShapeFactory::getOrCreateShapeFactory(xShapeFactory)->createGroup2D( xTarget ));
2545 // create the symbol
2546 Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio,
2547 xSymbolGroup, LegendSymbolStyle_LINE, xShapeFactory,
2548 Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ),
2549 VLegendSymbolFactory::PropertyType::Line, uno::Any() ));
2551 // set CID to symbol for selection
2552 if( xShape.is())
2554 aEntry.aSymbol.set( xSymbolGroup, uno::UNO_QUERY );
2556 bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurves[i] );
2557 ObjectType eObjectType = bAverageLine ? OBJECTTYPE_DATA_AVERAGE_LINE : OBJECTTYPE_DATA_CURVE;
2558 OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( eObjectType, i ) );
2559 aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) );
2560 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2561 AbstractShapeFactory::setShapeName( xShape, aCID );
2564 aResult.push_back(aEntry);
2569 catch( const uno::Exception & )
2571 DBG_UNHANDLED_EXCEPTION("chart2" );
2573 return aResult;
2576 VSeriesPlotter* VSeriesPlotter::createSeriesPlotter(
2577 const uno::Reference<XChartType>& xChartTypeModel
2578 , sal_Int32 nDimensionCount
2579 , bool bExcludingPositioning )
2581 if (!xChartTypeModel.is())
2582 return nullptr;
2584 OUString aChartType = xChartTypeModel->getChartType();
2586 VSeriesPlotter* pRet=nullptr;
2587 if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_COLUMN ) )
2588 pRet = new BarChart(xChartTypeModel,nDimensionCount);
2589 else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_BAR ) )
2590 pRet = new BarChart(xChartTypeModel,nDimensionCount);
2591 else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_AREA ) )
2592 pRet = new AreaChart(xChartTypeModel,nDimensionCount,true);
2593 else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_LINE ) )
2594 pRet = new AreaChart(xChartTypeModel,nDimensionCount,true,true);
2595 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER) )
2596 pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true);
2597 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE) )
2598 pRet = new BubbleChart(xChartTypeModel,nDimensionCount);
2599 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
2600 pRet = new PieChart(xChartTypeModel,nDimensionCount, bExcludingPositioning );
2601 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
2602 pRet = new NetChart(xChartTypeModel,nDimensionCount,true,new PolarPlottingPositionHelper());
2603 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
2604 pRet = new NetChart(xChartTypeModel,nDimensionCount,false,new PolarPlottingPositionHelper());
2605 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
2606 pRet = new CandleStickChart(xChartTypeModel,nDimensionCount);
2607 else
2608 pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true);
2609 return pRet;
2612 } //namespace chart
2614 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */