1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
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>
89 #include <unordered_map>
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)
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
)
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
))
146 , m_bCategoryXAxis(bCategoryXAxis
)
147 , m_nTimeResolution(css::chart::TimeUnit::DAY
)
148 , m_aNullDate(30,12,1899)
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();
169 for (auto const& elem
: m_aSecondaryPosHelperMap
)
171 PlottingPositionHelper
* pPosHelper
= elem
.second
;
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" );
189 if( m_pExplicitCategoriesProvider
&& m_pExplicitCategoriesProvider
->isDateAxis() )
190 pSeries
->setXValues( m_pExplicitCategoriesProvider
->getOriginalCategories() );
192 pSeries
->setCategoryXAxis();
196 if( m_pExplicitCategoriesProvider
)
197 pSeries
->setXValuesIfNone( m_pExplicitCategoriesProvider
->getOriginalCategories() );
200 if(zSlot
<0 || zSlot
>=static_cast<sal_Int32
>(m_aZSlots
.size()))
203 std::vector
< VDataSeriesGroup
> aZSlot
;
204 aZSlot
.emplace_back(pSeries
);
205 m_aZSlots
.push_back( aZSlot
);
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
);
219 //x slot is already occupied
220 //y slot decides what to do:
222 VDataSeriesGroup
& rYSlots
= rXSlots
[xSlot
];
223 sal_Int32 nYSlotCount
= rYSlots
.getSeriesCount();
227 //move all existing series in the xSlot to next slot
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
);
238 //y slot is already occupied
239 //insert at given y and x position
242 OSL_FAIL( "Not implemented yet");
248 drawing::Direction3D
VSeriesPlotter::getPreferredDiagramAspectRatio() const
250 drawing::Direction3D
aRet(1.0,1.0,1.0);
254 drawing::Direction3D
aScale( m_pPosHelper
->getScaledLogicWidth() );
255 aRet
.DirectionZ
= aScale
.DirectionZ
*0.2;
256 if(aRet
.DirectionZ
>1.0)
258 if(aRet
.DirectionZ
>10)
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
);
284 //create a group shape for this series and add to logic target:
285 xShapes
= createGroupShape( xTarget
,pDataSeries
->getCID() );
286 pDataSeries
->m_xGroupShape
= 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
);
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
;
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
);
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
;
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
);
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
;
338 uno::Reference
< drawing::XShapes
> VSeriesPlotter::getErrorBarsGroupShape( VDataSeries
& rDataSeries
339 , const uno::Reference
< drawing::XShapes
>& xTarget
342 uno::Reference
< css::drawing::XShapes
> &rShapeGroup
=
343 bYError
? rDataSeries
.m_xErrorYBarsGroupShape
: rDataSeries
.m_xErrorXBarsGroupShape
;
345 uno::Reference
< drawing::XShapes
> xShapes( rShapeGroup
);
348 //create a group shape for this series and add to logic target:
349 xShapes
= createGroupShape( xTarget
,rDataSeries
.getErrorBarsCID(bYError
) );
350 rShapeGroup
= xShapes
;
356 OUString
VSeriesPlotter::getLabelTextForValue( VDataSeries
const & rDataSeries
357 , sal_Int32 nPointIndex
359 , bool bAsPercentage
)
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
;
376 if( rDataSeries
.shouldLabelNumberFormatKeyBeDetectedFromYAxis() && m_aAxesNumberFormats
.hasFormat(1,rDataSeries
.getAttachedAxisIndex()) ) //y-axis
377 nNumberFormatKey
= m_aAxesNumberFormats
.getFormat(1,rDataSeries
.getAttachedAxisIndex());
379 nNumberFormatKey
= rDataSeries
.detectNumberFormatKey( nPointIndex
);
381 if(nNumberFormatKey
<0)
386 aNumber
= m_apNumberFormatterWrapper
->getFormattedString(
387 nNumberFormatKey
, fValue
, nLabelCol
, bColChanged
);
388 //@todo: change color of label if bColChanged is true
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
);
402 uno::Reference
< drawing::XShape
> VSeriesPlotter::createDataLabel( const uno::Reference
< drawing::XShapes
>& xTarget
403 , VDataSeries
& rDataSeries
404 , sal_Int32 nPointIndex
407 , const awt::Point
& rScreenPosition2D
408 , LabelAlignment eAlignment
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
);
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
) );
460 xProps
->getPropertyValue( "CharHeight") >>= fViewFontSize
;
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
) );
482 xSymbol
.set( VSeriesPlotter::createLegendSymbolForSeries( aMaxSymbolExtent
, rDataSeries
, xTarget_
, m_xShapeFactory
) );
486 bool bTextWrap
= false;
487 OUString
aSeparator(" ");
488 double fRotationDegrees
= 0.0;
491 uno::Reference
< beans::XPropertySet
> xPointProps( rDataSeries
.getPropertiesOfPoint( nPointIndex
) );
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;
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 );
527 case DataPointCustomLabelFieldType_CATEGORYNAME
:
529 aTextList
[i
] = getCategoryName( nPointIndex
);
532 case DataPointCustomLabelFieldType_SERIESNAME
:
535 if ( m_xChartTypeModel
)
536 aRole
= m_xChartTypeModel
->getRoleOfSequenceForSeriesLabel();
537 uno::Reference
< XDataSeries
> xSeries( rDataSeries
.getModel() );
538 aTextList
[i
] = DataSeriesHelper::getDataSeriesLabel( xSeries
, aRole
);
541 case DataPointCustomLabelFieldType_CELLREF
:
543 // TODO: for now doesn't show placeholder
544 aTextList
[i
] = OUString();
547 case DataPointCustomLabelFieldType_TEXT
:
549 aTextList
[i
] = aCustomLabels
[i
]->getString();
552 case DataPointCustomLabelFieldType_NEWLINE
:
560 aCustomLabels
[i
]->setString( aTextList
[i
] );
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
)
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
) )
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
;
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
;
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
;
649 xTextShape
= AbstractShapeFactory::getOrCreateShapeFactory(m_xShapeFactory
)->
650 createText( xTarget_
, aTextList
, aParaPropNames
, aParaPropValues
651 , *pPropNames
, *pPropValues
, AbstractShapeFactory::makeTransformation( aScreenPosition2D
) );
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
] );
670 xTextShape
= AbstractShapeFactory::getOrCreateShapeFactory(m_xShapeFactory
)->
671 createText( xTarget_
, aText
.makeStringAndClear()
672 , *pPropNames
, *pPropValues
, AbstractShapeFactory::makeTransformation( aScreenPosition2D
) );
675 if( !xTextShape
.is() )
678 // we need to use a default value for the maximum width property ?
679 if( nTextWidth
== 0 && bTextWrap
)
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
);
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
);
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
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
);
775 double lcl_getErrorBarLogicLength(
776 const uno::Sequence
< double > & rData
,
777 const uno::Reference
< beans::XPropertySet
>& xProp
,
778 sal_Int32 nErrorBarStyle
,
784 ::rtl::math::setNan( & fResult
);
787 switch( nErrorBarStyle
)
789 case css::chart::ErrorBarStyle::NONE
:
791 case css::chart::ErrorBarStyle::VARIANCE
:
792 fResult
= StatisticsHelper::getVariance( rData
);
794 case css::chart::ErrorBarStyle::STANDARD_DEVIATION
:
795 fResult
= StatisticsHelper::getStandardDeviation( rData
);
797 case css::chart::ErrorBarStyle::RELATIVE
:
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;
813 case css::chart::ErrorBarStyle::ABSOLUTE
:
814 xProp
->getPropertyValue( bPositive
815 ? OUString("PositiveError")
816 : OUString("NegativeError") ) >>= fResult
;
818 case css::chart::ErrorBarStyle::ERROR_MARGIN
:
820 // todo: check if this is really what's called error-margin
822 if( xProp
->getPropertyValue( bPositive
823 ? OUString("PositiveError")
824 : OUString("NegativeError") ) >>= fPercent
)
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
)
834 if( ::rtl::math::isFinite( fMaxValue
) &&
835 ::rtl::math::isFinite( fPercent
))
837 fResult
= fMaxValue
* fPercent
/ 100.0;
842 case css::chart::ErrorBarStyle::STANDARD_ERROR
:
843 fResult
= StatisticsHelper::getStandardError( rData
);
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
);
855 catch( const uno::Exception
& e
)
857 SAL_WARN("chart2", "Exception caught. " << e
);
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
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();
900 //main direction has constant x value
901 MinX
= rUnscaledLogicPosition
.PositionX
;
902 MaxX
= rUnscaledLogicPosition
.PositionX
;
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() )
921 return aMainDirection
;
924 drawing::Position3D
lcl_transformMixedToScene( PlottingPositionHelper
const * pPosHelper
925 , double fX
/*scaled*/, double fY
/*unscaled*/, double fZ
/*unscaled*/ )
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
942 , bool bYError
/* = true */
943 , const double* pfScaledLogicX
946 if( !ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel
, m_nDimension
) )
949 if( ! xErrorBarProperties
.is())
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
)
965 if(nErrorBarStyle
==css::chart::ErrorBarStyle::NONE
)
971 drawing::Position3D
aUnscaledLogicPosition(rUnscaledLogicPosition
);
972 if(nErrorBarStyle
==css::chart::ErrorBarStyle::STANDARD_DEVIATION
)
975 aUnscaledLogicPosition
.PositionY
= rVDataSeries
.getYMeanValue();
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
;
988 fScaledX
= *pfScaledLogicX
;
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() );
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
;
1009 aPositive
= lcl_transformMixedToScene( m_pPosHelper
, fScaledX
, fLocalY
, fZ
);
1014 aPositive
= m_pPosHelper
->transformLogicToScene( fLocalX
, fLocalY
, fZ
, true );
1016 bCreatePositiveBorder
= m_pPosHelper
->isLogicVisible(fLocalX
, fLocalY
, fZ
);
1019 bShowPositive
= false;
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
;
1032 aNegative
= lcl_transformMixedToScene( m_pPosHelper
, fScaledX
, fLocalY
, fZ
);
1037 aNegative
= m_pPosHelper
->transformLogicToScene( fLocalX
, fLocalY
, fZ
, true );
1039 bCreateNegativeBorder
= m_pPosHelper
->isLogicVisible( fLocalX
, fLocalY
, fZ
);
1042 bShowNegative
= false;
1045 if(!bShowPositive
&& !bShowNegative
)
1048 drawing::PolyPolygonShape3D aPoly
;
1050 sal_Int32 nSequenceIndex
=0;
1052 AddPointToPoly( aPoly
, aNegative
, nSequenceIndex
);
1053 AddPointToPoly( aPoly
, aMiddle
, nSequenceIndex
);
1055 AddPointToPoly( aPoly
, aPositive
, nSequenceIndex
);
1057 if( bShowNegative
&& bCreateNegativeBorder
)
1059 ::basegfx::B2DVector aMainDirection
= lcl_getErrorBarMainDirection( aMiddle
, aNegative
, m_pPosHelper
, aUnscaledLogicPosition
, bYError
);
1061 lcl_AddErrorBottomLine( aNegative
, aMainDirection
, aPoly
, nSequenceIndex
);
1063 if( bShowPositive
&& bCreatePositiveBorder
)
1065 ::basegfx::B2DVector aMainDirection
= lcl_getErrorBarMainDirection( aMiddle
, aPositive
, m_pPosHelper
, aUnscaledLogicPosition
, bYError
);
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
)
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 */
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
)
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 */
1123 void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries
const & rVDataSeries
,
1124 const uno::Reference
< drawing::XShapes
>& xTarget
,
1125 const uno::Reference
< drawing::XShapes
>& xEquationTarget
,
1126 bool bMaySkipPoints
)
1130 uno::Reference
< XRegressionCurveContainer
> xContainer( rVDataSeries
.getModel(), uno::UNO_QUERY
);
1131 if(!xContainer
.is())
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())
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;
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 )
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
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
;
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
,
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
);
1283 indexSep
= nStringLength
;
1284 sal_Int32 nLineLength
= indexSep
- i
;
1285 if ( nLineLength
> nMaxLineLength
)
1286 nMaxLineLength
= nLineLength
;
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())
1304 bool bShowEquation
= false;
1305 bool bShowCorrCoeff
= false;
1306 if(( xEquationProperties
->getPropertyValue( "ShowEquation") >>= bShowEquation
) &&
1307 ( xEquationProperties
->getPropertyValue( "ShowCorrelationCoefficient") >>= bShowCorrCoeff
))
1309 if( ! (bShowEquation
|| bShowCorrCoeff
))
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;
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
);
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())
1360 m_apNumberFormatterWrapper
->getFormattedString(
1361 nNumberFormatKey
, fR
*fR
, nLabelCol
, bColChanged
));
1362 //@todo: change color of label if bColChanged is true
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
));
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
;
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 )
1422 if( (aPos
.Y
+ aSize
.Height
) > m_aPageReferenceSize
.Height
)
1423 aPos
.Y
= m_aPageReferenceSize
.Height
- aSize
.Height
;
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
));
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
)
1486 double VSeriesPlotter::getMinimumX()
1488 double fMinimum
, fMaximum
;
1489 getMinimumAndMaximiumX( fMinimum
, fMaximum
);
1492 double VSeriesPlotter::getMaximumX()
1494 double fMinimum
, fMaximum
;
1495 getMinimumAndMaximiumX( fMinimum
, 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
);
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
);
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
);
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
);
1565 double VSeriesPlotter::getMinimumZ()
1567 //this is the default for all charts without a meaningful z axis
1570 double VSeriesPlotter::getMaximumZ()
1572 if( m_nDimension
!=3 || !m_aZSlots
.size() )
1573 return getMinimumZ()+1;
1574 return m_aZSlots
.size();
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
1667 for (auto const& ZSlot
: m_aZSlots
)
1669 for (auto const& XSlot
: ZSlot
)
1671 sal_Int32 nPointCount
= XSlot
.getPointCount();
1672 if( nPointCount
>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
;
1702 for (VDataSeries
* pSeries
: m_aSeriesVector
)
1704 sal_Int32 nPointCount
= pSeries
->getTotalPointCount();
1705 if( nPointCount
>nRet
)
1708 m_nMaxPointCount
=nRet
;
1709 m_aListOfCachedYValues
.clear();
1710 m_aListOfCachedYValues
.resize(m_nMaxPointCount
);
1711 m_bMaxPointCountDirty
=false;
1715 sal_Int32
VDataSeriesGroup::getAttachedAxisIndexForFirstSeries() const
1719 if (!m_aSeriesVector
.empty())
1720 nRet
= m_aSeriesVector
[0]->getAttachedAxisIndex();
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
) )
1745 if(::rtl::math::isInf(rfMinimum
))
1746 ::rtl::math::setNan(&rfMinimum
);
1747 if(::rtl::math::isInf(rfMaximum
))
1748 ::rtl::math::setNan(&rfMaximum
);
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
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
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
;
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.
1785 // This shouldn't happen!
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.
1800 // No existing pair. Insert a new one.
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
);
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
;
1832 * Parse all data and reduce them into a set of global Y value ranges per
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
));
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
>()));
1872 // insertion failed.
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.
1894 PerXMinMaxCalculator aRangeCalc
;
1895 for (const VDataSeries
* pSeries
: m_aSeriesVector
)
1900 for (sal_Int32 i
= 0, n
= pSeries
->getTotalPointCount(); i
< n
; ++i
)
1902 if (nAxisIndex
!= pSeries
->getAttachedAxisIndex())
1905 double fX
= pSeries
->getXValue(i
);
1906 if (rtl::math::isNan(fX
))
1909 if (fX
< fMinX
|| fX
> fMaxX
)
1910 // Outside specified X range. Skip it.
1913 double fY
= pSeries
->getYValue(i
);
1914 if (::rtl::math::isNan(fY
))
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())
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
;
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() )
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
;
1969 fPositiveSum
+= fValueMaxY
;
1971 if( fValueMinY
< 0 )
1973 if(::rtl::math::isNan( fNegativeSum
))
1974 fNegativeSum
= fFirstNegativeY
= fValueMinY
;
1976 fNegativeSum
+= fValueMinY
;
1979 rfMinimumY
= ::rtl::math::isNan( fNegativeSum
) ? fFirstPositiveY
: fNegativeSum
;
1980 rfMaximumY
= ::rtl::math::isNan( fPositiveSum
) ? fFirstNegativeY
: fPositiveSum
;
1984 for (const VDataSeries
* pSeries
: m_aSeriesVector
)
1986 if( nAxisIndex
!= pSeries
->getAttachedAxisIndex() )
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
;
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)
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
)
2062 m_aSecondaryValueScales
[nAxisIndex
]=rScale
;
2065 PlottingPositionHelper
& VSeriesPlotter::getPlottingPositionHelper( sal_Int32 nAxisIndex
) const
2067 PlottingPositionHelper
* pRet
= nullptr;
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
;
2086 pRet
= m_pMainPosHelper
;
2088 pRet
->setTimeResolution( m_nTimeResolution
, m_aNullDate
);
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];
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
];
2126 uno::Sequence
< OUString
> VSeriesPlotter::getSeriesNames() const
2128 std::vector
<OUString
> aRetVector
;
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 );
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
;
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
)
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())
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
2238 aResult
.insert( aResult
.begin(), aSeriesEntries
.begin(), aSeriesEntries
.end() );
2240 aResult
.insert( aResult
.end(), aSeriesEntries
.begin(), aSeriesEntries
.end() );
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());
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
);
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 )
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
) )
2325 if( bCurrentDashed
)
2327 bHasDashedLines
= true;
2332 bool bRegressionHasDashedLines
=false;
2333 if( lcl_HasRegressionCurves( *pSeries
, bRegressionHasDashedLines
) )
2336 if( bRegressionHasDashedLines
)
2338 bHasDashedLines
= true;
2345 if( bHasDashedLines
)
2346 aRet
= awt::Size(1600,-1);
2348 aRet
= awt::Size(800,-1);
2353 uno::Any
VSeriesPlotter::getExplicitSymbol( const VDataSeries
& /*rSeries*/, sal_Int32
/*nPointIndex*/ )
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
;
2381 Reference
< drawing::XShape
> xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio
,
2382 xTarget
, eLegendSymbolStyle
, xShapeFactory
2383 , rSeries
.getPropertiesOfSeries(), ePropType
, aExplicitSymbol
));
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
;
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
);
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
));
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() ) )
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
)
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
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
);
2489 aLabelText
= aCategoryNames
[nIdx
];
2490 if( xShape
.is() || !aLabelText
.isEmpty() )
2492 aEntry
.aLabel
= FormattedStringHelper::createFormattedStringSequence( xContext
, aLabelText
, xTextProperties
);
2493 aResult
.push_back(aEntry
);
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
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
);
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
))
2528 Reference
< XRegressionCurveContainer
> xRegrCont( rSeries
.getModel(), uno::UNO_QUERY
);
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() )
2538 OUString
aResStr( RegressionCurveHelper::getUINameForRegressionCurve( aCurves
[i
] ) );
2539 replaceParamterInString( aResStr
, "%SERIESNAME", aLabelText
);
2540 aEntry
.aLabel
= FormattedStringHelper::createFormattedStringSequence( xContext
, aResStr
, xTextProperties
);
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
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" );
2576 VSeriesPlotter
* VSeriesPlotter::createSeriesPlotter(
2577 const uno::Reference
<XChartType
>& xChartTypeModel
2578 , sal_Int32 nDimensionCount
2579 , bool bExcludingPositioning
)
2581 if (!xChartTypeModel
.is())
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
);
2608 pRet
= new AreaChart(xChartTypeModel
,nDimensionCount
,false,true);
2614 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */