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 <BaseGFXHelper.hxx>
23 #include <VLineProperties.hxx>
24 #include <ShapeFactory.hxx>
26 #include <CommonConverters.hxx>
27 #include <ExplicitCategoriesProvider.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>
44 #include <ChartModel.hxx>
46 //only for creation: @todo remove if all plotter are uno components and instantiated via servicefactory
47 #include "BarChart.hxx"
48 #include "PieChart.hxx"
49 #include "AreaChart.hxx"
50 #include "CandleStickChart.hxx"
51 #include "BubbleChart.hxx"
52 #include "NetChart.hxx"
53 #include <unonames.hxx>
54 #include <SpecialCharacters.hxx>
56 #include <com/sun/star/chart2/DataPointLabel.hpp>
57 #include <com/sun/star/chart/ErrorBarStyle.hpp>
58 #include <com/sun/star/chart/TimeUnit.hpp>
59 #include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
60 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
61 #include <com/sun/star/container/XChild.hpp>
62 #include <com/sun/star/chart2/RelativePosition.hpp>
63 #include <tools/color.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>
69 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
71 #include <com/sun/star/drawing/XShapes.hpp>
73 #include <unotools/localedatawrapper.hxx>
74 #include <comphelper/sequence.hxx>
75 #include <vcl/svapp.hxx>
76 #include <vcl/settings.hxx>
77 #include <tools/diagnose_ex.h>
78 #include <sal/log.hxx>
82 #include <unordered_map>
87 using namespace ::com::sun::star
;
88 using namespace ::com::sun::star::chart
;
89 using namespace ::com::sun::star::chart2
;
90 using ::com::sun::star::uno::Reference
;
91 using ::com::sun::star::uno::Sequence
;
93 VDataSeriesGroup::CachedYValues::CachedYValues()
94 : m_bValuesDirty(true)
100 VDataSeriesGroup::VDataSeriesGroup( std::unique_ptr
<VDataSeries
> pSeries
)
102 , m_bMaxPointCountDirty(true)
103 , m_nMaxPointCount(0)
104 , m_aListOfCachedYValues()
106 m_aSeriesVector
[0] = std::move(pSeries
);
109 VDataSeriesGroup::VDataSeriesGroup(VDataSeriesGroup
&& other
) noexcept
110 : m_aSeriesVector( std::move(other
.m_aSeriesVector
) )
111 , m_bMaxPointCountDirty( other
.m_bMaxPointCountDirty
)
112 , m_nMaxPointCount( other
.m_nMaxPointCount
)
113 , m_aListOfCachedYValues( std::move(other
.m_aListOfCachedYValues
) )
117 VDataSeriesGroup::~VDataSeriesGroup()
121 void VDataSeriesGroup::deleteSeries()
123 //delete all data series help objects:
124 m_aSeriesVector
.clear();
127 void VDataSeriesGroup::addSeries( std::unique_ptr
<VDataSeries
> pSeries
)
129 m_aSeriesVector
.push_back(std::move(pSeries
));
130 m_bMaxPointCountDirty
=true;
133 sal_Int32
VDataSeriesGroup::getSeriesCount() const
135 return m_aSeriesVector
.size();
138 VSeriesPlotter::VSeriesPlotter( const uno::Reference
<XChartType
>& xChartTypeModel
139 , sal_Int32 nDimensionCount
, bool bCategoryXAxis
)
140 : PlotterBase( nDimensionCount
)
141 , m_pMainPosHelper( nullptr )
142 , m_xChartTypeModel(xChartTypeModel
)
143 , m_xChartTypeModelProps( uno::Reference
< beans::XPropertySet
>::query( xChartTypeModel
))
145 , m_bCategoryXAxis(bCategoryXAxis
)
146 , m_nTimeResolution(css::chart::TimeUnit::DAY
)
147 , m_aNullDate(30,12,1899)
149 , m_pExplicitCategoriesProvider(nullptr)
150 , m_bPointsWereSkipped(false)
151 , m_bPieLabelsAllowToMove(false)
152 , m_aAvailableOuterRect(0, 0, 0, 0)
154 SAL_WARN_IF(!m_xChartTypeModel
.is(),"chart2","no XChartType available in view, fallback to default values may be wrong");
157 VSeriesPlotter::~VSeriesPlotter()
159 //delete all data series help objects:
160 for (std::vector
<VDataSeriesGroup
> & rGroupVector
: m_aZSlots
)
162 for (VDataSeriesGroup
& rGroup
: rGroupVector
)
164 rGroup
.deleteSeries();
166 rGroupVector
.clear();
170 m_aSecondaryPosHelperMap
.clear();
172 m_aSecondaryValueScales
.clear();
175 void VSeriesPlotter::addSeries( std::unique_ptr
<VDataSeries
> pSeries
, sal_Int32 zSlot
, sal_Int32 xSlot
, sal_Int32 ySlot
)
177 //take ownership of pSeries
179 OSL_PRECOND( pSeries
, "series to add is NULL" );
185 if( m_pExplicitCategoriesProvider
&& m_pExplicitCategoriesProvider
->isDateAxis() )
186 pSeries
->setXValues( m_pExplicitCategoriesProvider
->getOriginalCategories() );
188 pSeries
->setCategoryXAxis();
192 if( m_pExplicitCategoriesProvider
)
193 pSeries
->setXValuesIfNone( m_pExplicitCategoriesProvider
->getOriginalCategories() );
196 if(zSlot
<0 || zSlot
>=static_cast<sal_Int32
>(m_aZSlots
.size()))
199 std::vector
< VDataSeriesGroup
> aZSlot
;
200 aZSlot
.emplace_back( std::move(pSeries
) );
201 m_aZSlots
.push_back( std::move(aZSlot
) );
206 std::vector
< VDataSeriesGroup
>& rXSlots
= m_aZSlots
[zSlot
];
208 if(xSlot
<0 || xSlot
>=static_cast<sal_Int32
>(rXSlots
.size()))
210 //append the series to already existing x series
211 rXSlots
.emplace_back( std::move(pSeries
) );
215 //x slot is already occupied
216 //y slot decides what to do:
218 VDataSeriesGroup
& rYSlots
= rXSlots
[xSlot
];
219 sal_Int32 nYSlotCount
= rYSlots
.getSeriesCount();
223 //move all existing series in the xSlot to next slot
225 OSL_FAIL( "Not implemented yet");
227 else if( ySlot
== -1 || ySlot
>= nYSlotCount
)
229 //append the series to already existing y series
230 rYSlots
.addSeries( std::move(pSeries
) );
234 //y slot is already occupied
235 //insert at given y and x position
238 OSL_FAIL( "Not implemented yet");
244 drawing::Direction3D
VSeriesPlotter::getPreferredDiagramAspectRatio() const
246 drawing::Direction3D
aRet(1.0,1.0,1.0);
250 drawing::Direction3D
aScale( m_pPosHelper
->getScaledLogicWidth() );
251 aRet
.DirectionZ
= aScale
.DirectionZ
*0.2;
252 if(aRet
.DirectionZ
>1.0)
254 if(aRet
.DirectionZ
>10)
259 void VSeriesPlotter::releaseShapes()
261 for (std::vector
<VDataSeriesGroup
> const & rGroupVector
: m_aZSlots
)
263 for (VDataSeriesGroup
const & rGroup
: rGroupVector
)
265 //iterate through all series in this x slot
266 for (std::unique_ptr
<VDataSeries
> const & pSeries
: rGroup
.m_aSeriesVector
)
268 pSeries
->releaseShapes();
274 uno::Reference
< drawing::XShapes
> VSeriesPlotter::getSeriesGroupShape( VDataSeries
* pDataSeries
275 , const uno::Reference
< drawing::XShapes
>& xTarget
)
277 uno::Reference
< drawing::XShapes
> xShapes( pDataSeries
->m_xGroupShape
);
280 //create a group shape for this series and add to logic target:
281 xShapes
= createGroupShape( xTarget
,pDataSeries
->getCID() );
282 pDataSeries
->m_xGroupShape
= xShapes
;
287 uno::Reference
< drawing::XShapes
> VSeriesPlotter::getSeriesGroupShapeFrontChild( VDataSeries
* pDataSeries
288 , const uno::Reference
< drawing::XShapes
>& xTarget
)
290 uno::Reference
< drawing::XShapes
> xShapes( pDataSeries
->m_xFrontSubGroupShape
);
293 //ensure that the series group shape is already created
294 uno::Reference
< drawing::XShapes
> xSeriesShapes( getSeriesGroupShape( pDataSeries
, xTarget
) );
295 //ensure that the back child is created first
296 getSeriesGroupShapeBackChild( pDataSeries
, xTarget
);
297 //use series group shape as parent for the new created front group shape
298 xShapes
= createGroupShape( xSeriesShapes
);
299 pDataSeries
->m_xFrontSubGroupShape
= xShapes
;
304 uno::Reference
< drawing::XShapes
> VSeriesPlotter::getSeriesGroupShapeBackChild( VDataSeries
* pDataSeries
305 , const uno::Reference
< drawing::XShapes
>& xTarget
)
307 uno::Reference
< drawing::XShapes
> xShapes( pDataSeries
->m_xBackSubGroupShape
);
310 //ensure that the series group shape is already created
311 uno::Reference
< drawing::XShapes
> xSeriesShapes( getSeriesGroupShape( pDataSeries
, xTarget
) );
312 //use series group shape as parent for the new created back group shape
313 xShapes
= createGroupShape( xSeriesShapes
);
314 pDataSeries
->m_xBackSubGroupShape
= xShapes
;
319 uno::Reference
< drawing::XShapes
> VSeriesPlotter::getLabelsGroupShape( VDataSeries
& rDataSeries
320 , const uno::Reference
< drawing::XShapes
>& xTextTarget
)
322 //xTextTarget needs to be a 2D shape container always!
324 uno::Reference
< drawing::XShapes
> xShapes( rDataSeries
.m_xLabelsGroupShape
);
327 //create a 2D group shape for texts of this series and add to text target:
328 xShapes
= m_pShapeFactory
->createGroup2D( xTextTarget
, rDataSeries
.getLabelsCID() );
329 rDataSeries
.m_xLabelsGroupShape
= xShapes
;
334 uno::Reference
< drawing::XShapes
> VSeriesPlotter::getErrorBarsGroupShape( VDataSeries
& rDataSeries
335 , const uno::Reference
< drawing::XShapes
>& xTarget
338 uno::Reference
< css::drawing::XShapes
> &rShapeGroup
=
339 bYError
? rDataSeries
.m_xErrorYBarsGroupShape
: rDataSeries
.m_xErrorXBarsGroupShape
;
341 uno::Reference
< drawing::XShapes
> xShapes( rShapeGroup
);
344 //create a group shape for this series and add to logic target:
345 xShapes
= createGroupShape( xTarget
,rDataSeries
.getErrorBarsCID(bYError
) );
346 rShapeGroup
= xShapes
;
352 OUString
VSeriesPlotter::getLabelTextForValue( VDataSeries
const & rDataSeries
353 , sal_Int32 nPointIndex
355 , bool bAsPercentage
)
359 if (m_apNumberFormatterWrapper
)
361 sal_Int32 nNumberFormatKey
= 0;
362 if( rDataSeries
.hasExplicitNumberFormat(nPointIndex
,bAsPercentage
) )
363 nNumberFormatKey
= rDataSeries
.getExplicitNumberFormat(nPointIndex
,bAsPercentage
);
364 else if( bAsPercentage
)
366 sal_Int32 nPercentFormat
= DiagramHelper::getPercentNumberFormat( m_apNumberFormatterWrapper
->getNumberFormatsSupplier() );
367 if( nPercentFormat
!= -1 )
368 nNumberFormatKey
= nPercentFormat
;
372 nNumberFormatKey
= rDataSeries
.detectNumberFormatKey( nPointIndex
);
374 if(nNumberFormatKey
<0)
379 aNumber
= m_apNumberFormatterWrapper
->getFormattedString(
380 nNumberFormatKey
, fValue
, nLabelCol
, bColChanged
);
381 //@todo: change color of label if bColChanged is true
385 const LocaleDataWrapper
& rLocaleDataWrapper
= Application::GetSettings().GetLocaleDataWrapper();
386 const OUString
& aNumDecimalSep
= rLocaleDataWrapper
.getNumDecimalSep();
387 assert(aNumDecimalSep
.getLength() > 0);
388 sal_Unicode cDecSeparator
= aNumDecimalSep
[0];
389 aNumber
= ::rtl::math::doubleToUString( fValue
, rtl_math_StringFormat_G
/*rtl_math_StringFormat*/
390 , 3/*DecPlaces*/ , cDecSeparator
);
395 uno::Reference
< drawing::XShape
> VSeriesPlotter::createDataLabel( const uno::Reference
< drawing::XShapes
>& xTarget
396 , VDataSeries
& rDataSeries
397 , sal_Int32 nPointIndex
400 , const awt::Point
& rScreenPosition2D
401 , LabelAlignment eAlignment
403 , sal_Int32 nTextWidth
)
405 uno::Reference
< drawing::XShape
> xTextShape
;
406 Sequence
<uno::Reference
<XDataPointCustomLabelField
>> aCustomLabels
;
410 const uno::Reference
< css::beans::XPropertySet
>& xPropertySet(
411 rDataSeries
.getPropertiesOfPoint( nPointIndex
) );
412 if( xPropertySet
.is() )
414 uno::Any aAny
= xPropertySet
->getPropertyValue( CHART_UNONAME_CUSTOM_LABEL_FIELDS
);
415 if( aAny
.hasValue() )
417 aAny
>>= aCustomLabels
;
421 awt::Point
aScreenPosition2D(rScreenPosition2D
);
422 if(eAlignment
==LABEL_ALIGN_LEFT
)
423 aScreenPosition2D
.X
-= nOffset
;
424 else if(eAlignment
==LABEL_ALIGN_RIGHT
)
425 aScreenPosition2D
.X
+= nOffset
;
426 else if(eAlignment
==LABEL_ALIGN_TOP
)
427 aScreenPosition2D
.Y
-= nOffset
;
428 else if(eAlignment
==LABEL_ALIGN_BOTTOM
)
429 aScreenPosition2D
.Y
+= nOffset
;
431 uno::Reference
< drawing::XShapes
> xTarget_
=
432 m_pShapeFactory
->createGroup2D(
433 getLabelsGroupShape(rDataSeries
, xTarget
),
434 ObjectIdentifier::createPointCID( rDataSeries
.getLabelCID_Stub(), nPointIndex
));
436 //check whether the label needs to be created and how:
437 DataPointLabel
* pLabel
= rDataSeries
.getDataPointLabelIfLabel( nPointIndex
);
442 //prepare legend symbol
444 // get the font size for the label through the "CharHeight" property
445 // attached to the passed data series entry.
446 // (By tracing font height values it results that for pie chart the
447 // font size is not the same for all labels, but here no font size
448 // modification occurs).
449 float fViewFontSize( 10.0 );
451 uno::Reference
< beans::XPropertySet
> xProps( rDataSeries
.getPropertiesOfPoint( nPointIndex
) );
453 xProps
->getPropertyValue( "CharHeight") >>= fViewFontSize
;
455 fViewFontSize
*= (2540.0f
/ 72.0f
);
458 // the font height is used for computing the size of an optional legend
459 // symbol to be prepended to the text label.
460 Reference
< drawing::XShape
> xSymbol
;
461 if(pLabel
->ShowLegendSymbol
)
463 sal_Int32 nSymbolHeight
= static_cast< sal_Int32
>( fViewFontSize
* 0.6 );
464 awt::Size aCurrentRatio
= getPreferredLegendKeyAspectRatio();
465 sal_Int32 nSymbolWidth
= aCurrentRatio
.Width
;
466 if( aCurrentRatio
.Height
> 0 )
468 nSymbolWidth
= nSymbolHeight
* aCurrentRatio
.Width
/aCurrentRatio
.Height
;
470 awt::Size
aMaxSymbolExtent( nSymbolWidth
, nSymbolHeight
);
472 if( rDataSeries
.isVaryColorsByPoint() )
473 xSymbol
.set( VSeriesPlotter::createLegendSymbolForPoint( aMaxSymbolExtent
, rDataSeries
, nPointIndex
, xTarget_
, m_xShapeFactory
) );
475 xSymbol
.set( VSeriesPlotter::createLegendSymbolForSeries( aMaxSymbolExtent
, rDataSeries
, xTarget_
, m_xShapeFactory
) );
479 bool bTextWrap
= false;
480 OUString
aSeparator(" ");
481 double fRotationDegrees
= 0.0;
484 uno::Reference
< beans::XPropertySet
> xPointProps( rDataSeries
.getPropertiesOfPoint( nPointIndex
) );
487 xPointProps
->getPropertyValue( "TextWordWrap" ) >>= bTextWrap
;
488 xPointProps
->getPropertyValue( "LabelSeparator" ) >>= aSeparator
;
489 // Extract the optional text rotation through the
490 // "TextRotation" property attached to the passed data point.
491 xPointProps
->getPropertyValue( "TextRotation" ) >>= fRotationDegrees
;
494 catch( const uno::Exception
& )
496 TOOLS_WARN_EXCEPTION("chart2", "" );
499 sal_Int32 nLineCountForSymbolsize
= 0;
500 sal_uInt32 nTextListLength
= 3;
501 sal_uInt32 nCustomLabelsCount
= aCustomLabels
.getLength();
502 Sequence
< OUString
> aTextList( nTextListLength
);
504 bool bUseCustomLabel
= nCustomLabelsCount
> 0;
505 if( bUseCustomLabel
)
507 nTextListLength
= ( nCustomLabelsCount
> 3 ) ? nCustomLabelsCount
: 3;
509 aTextList
= Sequence
< OUString
>( nTextListLength
);
510 for( sal_uInt32 i
= 0; i
< nCustomLabelsCount
; ++i
)
512 switch( aCustomLabels
[i
]->getFieldType() )
514 case DataPointCustomLabelFieldType_VALUE
:
516 aTextList
[i
] = getLabelTextForValue( rDataSeries
, nPointIndex
, fValue
, false );
519 case DataPointCustomLabelFieldType_CATEGORYNAME
:
521 aTextList
[i
] = getCategoryName( nPointIndex
);
524 case DataPointCustomLabelFieldType_SERIESNAME
:
527 if ( m_xChartTypeModel
)
528 aRole
= m_xChartTypeModel
->getRoleOfSequenceForSeriesLabel();
529 const uno::Reference
< XDataSeries
>& xSeries( rDataSeries
.getModel() );
530 aTextList
[i
] = DataSeriesHelper::getDataSeriesLabel( xSeries
, aRole
);
533 case DataPointCustomLabelFieldType_PERCENTAGE
:
541 aTextList
[i
] = getLabelTextForValue(rDataSeries
, nPointIndex
, fValue
, true);
544 case DataPointCustomLabelFieldType_CELLREF
:
545 case DataPointCustomLabelFieldType_CELLRANGE
:
547 // TODO: for now doesn't show placeholder
548 aTextList
[i
] = OUString();
551 case DataPointCustomLabelFieldType_TEXT
:
553 aTextList
[i
] = aCustomLabels
[i
]->getString();
556 case DataPointCustomLabelFieldType_NEWLINE
:
564 aCustomLabels
[i
]->setString( aTextList
[i
] );
569 if( pLabel
->ShowCategoryName
)
571 aTextList
[0] = getCategoryName( nPointIndex
);
574 if( pLabel
->ShowNumber
)
576 aTextList
[1] = getLabelTextForValue(rDataSeries
, nPointIndex
, fValue
, false);
579 if( pLabel
->ShowNumberInPercent
)
587 aTextList
[2] = getLabelTextForValue(rDataSeries
, nPointIndex
, fValue
, true);
591 for( auto const & line
: std::as_const(aTextList
) )
593 if( !line
.isEmpty() )
595 ++nLineCountForSymbolsize
;
599 //prepare properties for multipropertyset-interface of shape
600 tNameSequence
* pPropNames
;
601 tAnySequence
* pPropValues
;
602 if( !rDataSeries
.getTextLabelMultiPropertyLists( nPointIndex
, pPropNames
, pPropValues
) )
605 // set text alignment for the text shape to be created.
606 LabelPositionHelper::changeTextAdjustment( *pPropValues
, *pPropNames
, eAlignment
);
608 // check if data series entry percent value and absolute value have to
609 // be appended to the text label, and what should be the separator
610 // character (comma, space, new line). In case it is a new line we get
611 // a multi-line label.
612 bool bMultiLineLabel
= ( aSeparator
== "\n" );
614 if( bUseCustomLabel
)
616 Sequence
< uno::Reference
< XFormattedString
> > aFormattedLabels( aCustomLabels
.getLength() );
617 for( int i
= 0; i
< aFormattedLabels
.getLength(); i
++ )
619 aFormattedLabels
[i
] = aCustomLabels
[i
];
623 xTextShape
= ShapeFactory::getOrCreateShapeFactory( m_xShapeFactory
)->
624 createText( xTarget_
, aFormattedLabels
, *pPropNames
, *pPropValues
,
625 ShapeFactory::makeTransformation( aScreenPosition2D
) );
629 // join text list elements
630 OUStringBuffer aText
;
631 for( sal_uInt32 nN
= 0; nN
< nTextListLength
; ++nN
)
633 if( !aTextList
[nN
].isEmpty() )
635 if( !aText
.isEmpty() )
637 aText
.append(aSeparator
);
639 aText
.append( aTextList
[nN
] );
644 xTextShape
= ShapeFactory::getOrCreateShapeFactory(m_xShapeFactory
)->
645 createText( xTarget_
, aText
.makeStringAndClear(), *pPropNames
, *pPropValues
,
646 ShapeFactory::makeTransformation( aScreenPosition2D
) );
649 if( !xTextShape
.is() )
652 // we need to use a default value for the maximum width property ?
653 if( nTextWidth
== 0 && bTextWrap
)
656 (m_aPageReferenceSize
.Height
< m_aPageReferenceSize
.Width
)
657 ? m_aPageReferenceSize
.Height
658 : m_aPageReferenceSize
.Width
;
659 nTextWidth
= nMinSize
/ 3;
662 // in case text must be wrapped set the maximum width property
663 // for the text shape
664 if( nTextWidth
!= 0 && bTextWrap
)
666 uno::Reference
< beans::XPropertySet
> xProp( xTextShape
, uno::UNO_QUERY
);
669 // compute the height of a line of text
670 if( !bMultiLineLabel
|| nLineCountForSymbolsize
<= 0 )
672 nLineCountForSymbolsize
= 1;
674 awt::Size aTextSize
= xTextShape
->getSize();
675 sal_Int32 aTextLineHeight
= aTextSize
.Height
/ nLineCountForSymbolsize
;
677 // set maximum text width
678 uno::Any
aTextMaximumFrameWidth( nTextWidth
);
679 xProp
->setPropertyValue( "TextMaximumFrameWidth", aTextMaximumFrameWidth
);
681 // compute the total lines of text
682 aTextSize
= xTextShape
->getSize();
683 nLineCountForSymbolsize
= aTextSize
.Height
/ aTextLineHeight
;
687 // in case text is rotated, the transformation property of the text
688 // shape is modified.
689 if( fRotationDegrees
!= 0.0 )
691 const double fDegreesPi( -basegfx::deg2rad(fRotationDegrees
) );
692 uno::Reference
< beans::XPropertySet
> xProp( xTextShape
, uno::UNO_QUERY
);
694 xProp
->setPropertyValue( "Transformation", ShapeFactory::makeTransformation( aScreenPosition2D
, fDegreesPi
) );
695 LabelPositionHelper::correctPositionForRotation( xTextShape
, eAlignment
, fRotationDegrees
, true /*bRotateAroundCenter*/ );
698 awt::Point
aTextShapePos(xTextShape
->getPosition());
699 if( m_bPieLabelsAllowToMove
&& rDataSeries
.isLabelCustomPos(nPointIndex
) )
701 awt::Point aRelPos
= rDataSeries
.getLabelPosition(aTextShapePos
, nPointIndex
);
702 if( aRelPos
.X
!= -1 )
704 xTextShape
->setPosition(aRelPos
);
705 if( !m_xChartTypeModel
->getChartType().equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE
) &&
706 rDataSeries
.getPropertiesOfSeries()->getPropertyValue( "ShowCustomLeaderLines" ).get
<sal_Bool
>())
708 sal_Int32 nX1
= rScreenPosition2D
.X
;
709 sal_Int32 nY1
= rScreenPosition2D
.Y
;
712 ::basegfx::B2IRectangle
aRect(BaseGFXHelper::makeRectangle(aRelPos
, xTextShape
->getSize()));
715 else if (nX1
> aRect
.getMaxX())
716 nX2
= aRect
.getMaxX();
718 if (nY1
< aRect
.getMinY())
719 nY2
= aRect
.getMinY();
720 else if (nY1
> aRect
.getMaxY())
721 nY2
= aRect
.getMaxY();
723 //when the line is very short compared to the page size don't create one
724 ::basegfx::B2DVector
aLength(nX1
- nX2
, nY1
- nY2
);
725 double fPageDiagonaleLength
= sqrt(double(m_aPageReferenceSize
.Width
)*double(m_aPageReferenceSize
.Width
) + double(m_aPageReferenceSize
.Height
)*double(m_aPageReferenceSize
.Height
));
726 if ((aLength
.getLength() / fPageDiagonaleLength
) >= 0.01)
728 drawing::PointSequenceSequence
aPoints(1);
729 aPoints
[0].realloc(2);
730 aPoints
[0][0].X
= nX1
;
731 aPoints
[0][0].Y
= nY1
;
732 aPoints
[0][1].X
= nX2
;
733 aPoints
[0][1].Y
= nY2
;
735 VLineProperties aVLineProperties
;
736 m_pShapeFactory
->createLine2D(xTarget
, aPoints
, &aVLineProperties
);
742 // in case legend symbol has to be displayed, text shape position is
744 const awt::Point
aUnrotatedTextPos(xTextShape
->getPosition());
747 const awt::Point
aOldTextPos( xTextShape
->getPosition() );
748 awt::Point
aNewTextPos( aOldTextPos
);
750 awt::Point
aSymbolPosition( aUnrotatedTextPos
);
751 awt::Size
aSymbolSize( xSymbol
->getSize() );
752 awt::Size aTextSize
= xTextShape
->getSize();
754 sal_Int32 nXDiff
= aSymbolSize
.Width
+ static_cast< sal_Int32
>( std::max( 100.0, fViewFontSize
* 0.22 ) );//minimum 1mm
755 if( !bMultiLineLabel
|| nLineCountForSymbolsize
<= 0 )
756 nLineCountForSymbolsize
= 1;
757 aSymbolPosition
.Y
+= ((aTextSize
.Height
/nLineCountForSymbolsize
)/4);
759 if(eAlignment
==LABEL_ALIGN_LEFT
760 || eAlignment
==LABEL_ALIGN_LEFT_TOP
761 || eAlignment
==LABEL_ALIGN_LEFT_BOTTOM
)
763 aSymbolPosition
.X
-= nXDiff
;
765 else if(eAlignment
==LABEL_ALIGN_RIGHT
766 || eAlignment
==LABEL_ALIGN_RIGHT_TOP
767 || eAlignment
==LABEL_ALIGN_RIGHT_BOTTOM
)
769 aNewTextPos
.X
+= nXDiff
;
771 else if(eAlignment
==LABEL_ALIGN_TOP
772 || eAlignment
==LABEL_ALIGN_BOTTOM
773 || eAlignment
==LABEL_ALIGN_CENTER
)
775 aSymbolPosition
.X
-= nXDiff
/2;
776 aNewTextPos
.X
+= nXDiff
/2;
779 xSymbol
->setPosition( aSymbolPosition
);
780 xTextShape
->setPosition( aNewTextPos
);
783 catch( const uno::Exception
& )
785 TOOLS_WARN_EXCEPTION("chart2", "" );
793 double lcl_getErrorBarLogicLength(
794 const uno::Sequence
< double > & rData
,
795 const uno::Reference
< beans::XPropertySet
>& xProp
,
796 sal_Int32 nErrorBarStyle
,
802 ::rtl::math::setNan( & fResult
);
805 switch( nErrorBarStyle
)
807 case css::chart::ErrorBarStyle::NONE
:
809 case css::chart::ErrorBarStyle::VARIANCE
:
810 fResult
= StatisticsHelper::getVariance( rData
);
812 case css::chart::ErrorBarStyle::STANDARD_DEVIATION
:
813 fResult
= StatisticsHelper::getStandardDeviation( rData
);
815 case css::chart::ErrorBarStyle::RELATIVE
:
818 if( xProp
->getPropertyValue( bPositive
819 ? OUString("PositiveError")
820 : OUString("NegativeError") ) >>= fPercent
)
822 if( nIndex
>=0 && nIndex
< rData
.getLength() &&
823 ! std::isnan( rData
[nIndex
] ) &&
824 ! std::isnan( fPercent
))
826 fResult
= rData
[nIndex
] * fPercent
/ 100.0;
831 case css::chart::ErrorBarStyle::ABSOLUTE
:
832 xProp
->getPropertyValue( bPositive
833 ? OUString("PositiveError")
834 : OUString("NegativeError") ) >>= fResult
;
836 case css::chart::ErrorBarStyle::ERROR_MARGIN
:
838 // todo: check if this is really what's called error-margin
840 if( xProp
->getPropertyValue( bPositive
841 ? OUString("PositiveError")
842 : OUString("NegativeError") ) >>= fPercent
)
845 ::rtl::math::setInf(&fMaxValue
, true);
846 for(double d
: rData
)
851 if( std::isfinite( fMaxValue
) &&
852 std::isfinite( fPercent
))
854 fResult
= fMaxValue
* fPercent
/ 100.0;
859 case css::chart::ErrorBarStyle::STANDARD_ERROR
:
860 fResult
= StatisticsHelper::getStandardError( rData
);
862 case css::chart::ErrorBarStyle::FROM_DATA
:
864 uno::Reference
< chart2::data::XDataSource
> xErrorBarData( xProp
, uno::UNO_QUERY
);
865 if( xErrorBarData
.is())
866 fResult
= StatisticsHelper::getErrorFromDataSource(
867 xErrorBarData
, nIndex
, bPositive
, bYError
);
872 catch( const uno::Exception
& )
874 TOOLS_WARN_EXCEPTION("chart2", "" );
880 void lcl_AddErrorBottomLine( const drawing::Position3D
& rPosition
, ::basegfx::B2DVector aMainDirection
881 , drawing::PolyPolygonShape3D
& rPoly
, sal_Int32 nSequenceIndex
)
883 double fFixedWidth
= 200.0;
885 aMainDirection
.normalize();
886 ::basegfx::B2DVector
aOrthoDirection(-aMainDirection
.getY(),aMainDirection
.getX());
887 aOrthoDirection
.normalize();
889 ::basegfx::B2DVector
aAnchor( rPosition
.PositionX
, rPosition
.PositionY
);
890 ::basegfx::B2DVector aStart
= aAnchor
+ aOrthoDirection
*fFixedWidth
/2.0;
891 ::basegfx::B2DVector aEnd
= aAnchor
- aOrthoDirection
*fFixedWidth
/2.0;
893 AddPointToPoly( rPoly
, drawing::Position3D( aStart
.getX(), aStart
.getY(), rPosition
.PositionZ
), nSequenceIndex
);
894 AddPointToPoly( rPoly
, drawing::Position3D( aEnd
.getX(), aEnd
.getY(), rPosition
.PositionZ
), nSequenceIndex
);
897 ::basegfx::B2DVector
lcl_getErrorBarMainDirection(
898 const drawing::Position3D
& rStart
899 , const drawing::Position3D
& rBottomEnd
900 , PlottingPositionHelper
const * pPosHelper
901 , const drawing::Position3D
& rUnscaledLogicPosition
904 ::basegfx::B2DVector
aMainDirection( rStart
.PositionX
- rBottomEnd
.PositionX
905 , rStart
.PositionY
- rBottomEnd
.PositionY
);
906 if( !aMainDirection
.getLength() )
908 //get logic clip values:
909 double MinX
= pPosHelper
->getLogicMinX();
910 double MinY
= pPosHelper
->getLogicMinY();
911 double MaxX
= pPosHelper
->getLogicMaxX();
912 double MaxY
= pPosHelper
->getLogicMaxY();
913 double fZ
= pPosHelper
->getLogicMinZ();
917 //main direction has constant x value
918 MinX
= rUnscaledLogicPosition
.PositionX
;
919 MaxX
= rUnscaledLogicPosition
.PositionX
;
923 //main direction has constant y value
924 MinY
= rUnscaledLogicPosition
.PositionY
;
925 MaxY
= rUnscaledLogicPosition
.PositionY
;
928 drawing::Position3D aStart
= pPosHelper
->transformLogicToScene( MinX
, MinY
, fZ
, false );
929 drawing::Position3D aEnd
= pPosHelper
->transformLogicToScene( MaxX
, MaxY
, fZ
, false );
931 aMainDirection
= ::basegfx::B2DVector( aStart
.PositionX
- aEnd
.PositionX
932 , aStart
.PositionY
- aEnd
.PositionY
);
934 if( !aMainDirection
.getLength() )
938 return aMainDirection
;
941 drawing::Position3D
lcl_transformMixedToScene( PlottingPositionHelper
const * pPosHelper
942 , double fX
/*scaled*/, double fY
/*unscaled*/, double fZ
/*unscaled*/ )
945 return drawing::Position3D(0,0,0);
946 pPosHelper
->doLogicScaling( nullptr,&fY
,&fZ
);
947 pPosHelper
->clipScaledLogicValues( &fX
,&fY
,&fZ
);
948 return pPosHelper
->transformScaledLogicToScene( fX
, fY
, fZ
, false );
951 } // anonymous namespace
953 void VSeriesPlotter::createErrorBar(
954 const uno::Reference
< drawing::XShapes
>& xTarget
955 , const drawing::Position3D
& rUnscaledLogicPosition
956 , const uno::Reference
< beans::XPropertySet
> & xErrorBarProperties
957 , const VDataSeries
& rVDataSeries
959 , bool bYError
/* = true */
960 , const double* pfScaledLogicX
963 if( !ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel
, m_nDimension
) )
966 if( ! xErrorBarProperties
.is())
971 bool bShowPositive
= false;
972 bool bShowNegative
= false;
973 sal_Int32 nErrorBarStyle
= css::chart::ErrorBarStyle::VARIANCE
;
975 xErrorBarProperties
->getPropertyValue( "ShowPositiveError") >>= bShowPositive
;
976 xErrorBarProperties
->getPropertyValue( "ShowNegativeError") >>= bShowNegative
;
977 xErrorBarProperties
->getPropertyValue( "ErrorBarStyle") >>= nErrorBarStyle
;
979 if(!bShowPositive
&& !bShowNegative
)
982 if(nErrorBarStyle
==css::chart::ErrorBarStyle::NONE
)
988 drawing::Position3D
aUnscaledLogicPosition(rUnscaledLogicPosition
);
989 if(nErrorBarStyle
==css::chart::ErrorBarStyle::STANDARD_DEVIATION
)
992 aUnscaledLogicPosition
.PositionY
= rVDataSeries
.getYMeanValue();
994 aUnscaledLogicPosition
.PositionX
= rVDataSeries
.getXMeanValue();
997 bool bCreateNegativeBorder
= false;//make a vertical line at the negative end of the error bar
998 bool bCreatePositiveBorder
= false;//make a vertical line at the positive end of the error bar
999 drawing::Position3D
aMiddle(aUnscaledLogicPosition
);
1000 const double fX
= aUnscaledLogicPosition
.PositionX
;
1001 const double fY
= aUnscaledLogicPosition
.PositionY
;
1002 const double fZ
= aUnscaledLogicPosition
.PositionZ
;
1003 double fScaledX
= fX
;
1004 if( pfScaledLogicX
)
1005 fScaledX
= *pfScaledLogicX
;
1007 m_pPosHelper
->doLogicScaling( &fScaledX
, nullptr, nullptr );
1009 aMiddle
= lcl_transformMixedToScene( m_pPosHelper
, fScaledX
, fY
, fZ
);
1011 drawing::Position3D
aNegative(aMiddle
);
1012 drawing::Position3D
aPositive(aMiddle
);
1014 uno::Sequence
< double > aData( bYError
? rVDataSeries
.getAllY() : rVDataSeries
.getAllX() );
1018 double fLength
= lcl_getErrorBarLogicLength( aData
, xErrorBarProperties
, nErrorBarStyle
, nIndex
, true, bYError
);
1019 if( std::isfinite( fLength
) )
1021 double fLocalX
= fX
;
1022 double fLocalY
= fY
;
1026 aPositive
= lcl_transformMixedToScene( m_pPosHelper
, fScaledX
, fLocalY
, fZ
);
1031 aPositive
= m_pPosHelper
->transformLogicToScene( fLocalX
, fLocalY
, fZ
, true );
1033 bCreatePositiveBorder
= m_pPosHelper
->isLogicVisible(fLocalX
, fLocalY
, fZ
);
1036 bShowPositive
= false;
1041 double fLength
= lcl_getErrorBarLogicLength( aData
, xErrorBarProperties
, nErrorBarStyle
, nIndex
, false, bYError
);
1042 if( std::isfinite( fLength
) )
1044 double fLocalX
= fX
;
1045 double fLocalY
= fY
;
1049 aNegative
= lcl_transformMixedToScene( m_pPosHelper
, fScaledX
, fLocalY
, fZ
);
1054 aNegative
= m_pPosHelper
->transformLogicToScene( fLocalX
, fLocalY
, fZ
, true );
1056 bCreateNegativeBorder
= m_pPosHelper
->isLogicVisible( fLocalX
, fLocalY
, fZ
);
1059 bShowNegative
= false;
1062 if(!bShowPositive
&& !bShowNegative
)
1065 drawing::PolyPolygonShape3D aPoly
;
1067 sal_Int32 nSequenceIndex
=0;
1069 AddPointToPoly( aPoly
, aNegative
, nSequenceIndex
);
1070 AddPointToPoly( aPoly
, aMiddle
, nSequenceIndex
);
1072 AddPointToPoly( aPoly
, aPositive
, nSequenceIndex
);
1074 if( bShowNegative
&& bCreateNegativeBorder
)
1076 ::basegfx::B2DVector aMainDirection
= lcl_getErrorBarMainDirection( aMiddle
, aNegative
, m_pPosHelper
, aUnscaledLogicPosition
, bYError
);
1078 lcl_AddErrorBottomLine( aNegative
, aMainDirection
, aPoly
, nSequenceIndex
);
1080 if( bShowPositive
&& bCreatePositiveBorder
)
1082 ::basegfx::B2DVector aMainDirection
= lcl_getErrorBarMainDirection( aMiddle
, aPositive
, m_pPosHelper
, aUnscaledLogicPosition
, bYError
);
1084 lcl_AddErrorBottomLine( aPositive
, aMainDirection
, aPoly
, nSequenceIndex
);
1087 uno::Reference
< drawing::XShape
> xShape
= m_pShapeFactory
->createLine2D( xTarget
, PolyToPointSequence( aPoly
) );
1088 setMappedProperties( xShape
, xErrorBarProperties
, PropertyMapper::getPropertyNameMapForLineProperties() );
1090 catch( const uno::Exception
& )
1092 TOOLS_WARN_EXCEPTION("chart2", "" );
1097 void VSeriesPlotter::addErrorBorder(
1098 const drawing::Position3D
& rPos0
1099 ,const drawing::Position3D
& rPos1
1100 ,const uno::Reference
< drawing::XShapes
>& rTarget
1101 ,const uno::Reference
< beans::XPropertySet
>& rErrorBorderProp
)
1103 drawing::PolyPolygonShape3D aPoly
;
1104 sal_Int32 nSequenceIndex
= 0;
1105 AddPointToPoly( aPoly
, rPos0
, nSequenceIndex
);
1106 AddPointToPoly( aPoly
, rPos1
, nSequenceIndex
);
1107 uno::Reference
< drawing::XShape
> xShape
= m_pShapeFactory
->createLine2D(
1108 rTarget
, PolyToPointSequence( aPoly
) );
1109 setMappedProperties( xShape
, rErrorBorderProp
,
1110 PropertyMapper::getPropertyNameMapForLineProperties() );
1113 void VSeriesPlotter::createErrorRectangle(
1114 const drawing::Position3D
& rUnscaledLogicPosition
1115 ,VDataSeries
& rVDataSeries
1117 ,const uno::Reference
< drawing::XShapes
>& rTarget
1118 ,bool bUseXErrorData
1119 ,bool bUseYErrorData
)
1121 if ( m_nDimension
!= 2 )
1124 // error border properties
1125 Reference
< beans::XPropertySet
> xErrorBorderPropX
, xErrorBorderPropY
;
1126 if ( bUseXErrorData
)
1128 xErrorBorderPropX
= rVDataSeries
.getXErrorBarProperties( nIndex
);
1129 if ( !xErrorBorderPropX
.is() )
1132 uno::Reference
< drawing::XShapes
> xErrorBorder_ShapesX(
1133 getErrorBarsGroupShape( rVDataSeries
, rTarget
, false ) );
1135 if ( bUseYErrorData
)
1137 xErrorBorderPropY
= rVDataSeries
.getYErrorBarProperties( nIndex
);
1138 if ( !xErrorBorderPropY
.is() )
1141 uno::Reference
< drawing::XShapes
> xErrorBorder_ShapesY(
1142 getErrorBarsGroupShape( rVDataSeries
, rTarget
, true ) );
1144 if( !ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel
, m_nDimension
) )
1149 bool bShowXPositive
= false;
1150 bool bShowXNegative
= false;
1151 bool bShowYPositive
= false;
1152 bool bShowYNegative
= false;
1154 sal_Int32 nErrorBorderStyleX
= css::chart::ErrorBarStyle::VARIANCE
;
1155 sal_Int32 nErrorBorderStyleY
= css::chart::ErrorBarStyle::VARIANCE
;
1157 if ( bUseXErrorData
)
1159 xErrorBorderPropX
->getPropertyValue( "ErrorBarStyle" ) >>= nErrorBorderStyleX
;
1160 xErrorBorderPropX
->getPropertyValue( "ShowPositiveError") >>= bShowXPositive
;
1161 xErrorBorderPropX
->getPropertyValue( "ShowNegativeError") >>= bShowXNegative
;
1163 if ( bUseYErrorData
)
1165 xErrorBorderPropY
->getPropertyValue( "ErrorBarStyle" ) >>= nErrorBorderStyleY
;
1166 xErrorBorderPropY
->getPropertyValue( "ShowPositiveError") >>= bShowYPositive
;
1167 xErrorBorderPropY
->getPropertyValue( "ShowNegativeError") >>= bShowYNegative
;
1170 if ( bUseXErrorData
&& nErrorBorderStyleX
== css::chart::ErrorBarStyle::NONE
)
1171 bUseXErrorData
= false;
1172 if ( bUseYErrorData
&& nErrorBorderStyleY
== css::chart::ErrorBarStyle::NONE
)
1173 bUseYErrorData
= false;
1175 if ( !bShowXPositive
&& !bShowXNegative
&& !bShowYPositive
&& !bShowYNegative
)
1178 if ( !m_pPosHelper
)
1181 drawing::Position3D
aUnscaledLogicPosition( rUnscaledLogicPosition
);
1182 if ( bUseXErrorData
&& nErrorBorderStyleX
== css::chart::ErrorBarStyle::STANDARD_DEVIATION
)
1183 aUnscaledLogicPosition
.PositionX
= rVDataSeries
.getXMeanValue();
1184 if ( bUseYErrorData
&& nErrorBorderStyleY
== css::chart::ErrorBarStyle::STANDARD_DEVIATION
)
1185 aUnscaledLogicPosition
.PositionY
= rVDataSeries
.getYMeanValue();
1187 const double fX
= aUnscaledLogicPosition
.PositionX
;
1188 const double fY
= aUnscaledLogicPosition
.PositionY
;
1189 const double fZ
= aUnscaledLogicPosition
.PositionZ
;
1190 double fScaledX
= fX
;
1191 m_pPosHelper
->doLogicScaling( &fScaledX
, nullptr, nullptr );
1193 uno::Sequence
< double > aDataX( rVDataSeries
.getAllX() );
1194 uno::Sequence
< double > aDataY( rVDataSeries
.getAllY() );
1200 if ( bUseXErrorData
)
1202 if ( bShowXPositive
)
1203 fPosX
= lcl_getErrorBarLogicLength( aDataX
, xErrorBorderPropX
,
1204 nErrorBorderStyleX
, nIndex
, true, false );
1205 if ( bShowXNegative
)
1206 fNegX
= lcl_getErrorBarLogicLength( aDataX
, xErrorBorderPropX
,
1207 nErrorBorderStyleX
, nIndex
, false, false );
1210 if ( bUseYErrorData
)
1212 if ( bShowYPositive
)
1213 fPosY
= lcl_getErrorBarLogicLength( aDataY
, xErrorBorderPropY
,
1214 nErrorBorderStyleY
, nIndex
, true, true );
1215 if ( bShowYNegative
)
1216 fNegY
= lcl_getErrorBarLogicLength( aDataY
, xErrorBorderPropY
,
1217 nErrorBorderStyleY
, nIndex
, false, true );
1220 if ( !( std::isfinite( fPosX
) &&
1221 std::isfinite( fPosY
) &&
1222 std::isfinite( fNegX
) &&
1223 std::isfinite( fNegY
) ) )
1226 drawing::Position3D
aBottomLeft( lcl_transformMixedToScene( m_pPosHelper
,
1227 fX
- fNegX
, fY
- fNegY
, fZ
) );
1228 drawing::Position3D
aTopLeft( lcl_transformMixedToScene( m_pPosHelper
,
1229 fX
- fNegX
, fY
+ fPosY
, fZ
) );
1230 drawing::Position3D
aTopRight( lcl_transformMixedToScene( m_pPosHelper
,
1231 fX
+ fPosX
, fY
+ fPosY
, fZ
) );
1232 drawing::Position3D
aBottomRight( lcl_transformMixedToScene( m_pPosHelper
,
1233 fX
+ fPosX
, fY
- fNegY
, fZ
) );
1234 if ( bUseXErrorData
)
1237 addErrorBorder( aTopLeft
, aTopRight
, xErrorBorder_ShapesX
, xErrorBorderPropX
);
1240 addErrorBorder( aBottomRight
, aBottomLeft
, xErrorBorder_ShapesX
, xErrorBorderPropX
);
1243 if ( bUseYErrorData
)
1246 addErrorBorder( aBottomLeft
, aTopLeft
, xErrorBorder_ShapesY
, xErrorBorderPropY
);
1249 addErrorBorder( aTopRight
, aBottomRight
, xErrorBorder_ShapesY
, xErrorBorderPropY
);
1252 catch( const uno::Exception
& )
1254 DBG_UNHANDLED_EXCEPTION("chart2", "Exception in createErrorRectangle(). ");
1258 void VSeriesPlotter::createErrorBar_X( const drawing::Position3D
& rUnscaledLogicPosition
1259 , VDataSeries
& rVDataSeries
, sal_Int32 nPointIndex
1260 , const uno::Reference
< drawing::XShapes
>& xTarget
)
1265 uno::Reference
< beans::XPropertySet
> xErrorBarProp(rVDataSeries
.getXErrorBarProperties(nPointIndex
));
1266 if( xErrorBarProp
.is())
1268 uno::Reference
< drawing::XShapes
> xErrorBarsGroup_Shapes(
1269 getErrorBarsGroupShape(rVDataSeries
, xTarget
, false) );
1271 createErrorBar( xErrorBarsGroup_Shapes
1272 , rUnscaledLogicPosition
, xErrorBarProp
1273 , rVDataSeries
, nPointIndex
1274 , false /* bYError */
1279 void VSeriesPlotter::createErrorBar_Y( const drawing::Position3D
& rUnscaledLogicPosition
1280 , VDataSeries
& rVDataSeries
, sal_Int32 nPointIndex
1281 , const uno::Reference
< drawing::XShapes
>& xTarget
1282 , double const * pfScaledLogicX
)
1287 uno::Reference
< beans::XPropertySet
> xErrorBarProp(rVDataSeries
.getYErrorBarProperties(nPointIndex
));
1288 if( xErrorBarProp
.is())
1290 uno::Reference
< drawing::XShapes
> xErrorBarsGroup_Shapes(
1291 getErrorBarsGroupShape(rVDataSeries
, xTarget
, true) );
1293 createErrorBar( xErrorBarsGroup_Shapes
1294 , rUnscaledLogicPosition
, xErrorBarProp
1295 , rVDataSeries
, nPointIndex
1296 , true /* bYError */
1301 void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries
const & rVDataSeries
,
1302 const uno::Reference
< drawing::XShapes
>& xTarget
,
1303 const uno::Reference
< drawing::XShapes
>& xEquationTarget
,
1304 bool bMaySkipPoints
)
1308 uno::Reference
< XRegressionCurveContainer
> xContainer( rVDataSeries
.getModel(), uno::UNO_QUERY
);
1309 if(!xContainer
.is())
1315 uno::Sequence
< uno::Reference
< XRegressionCurve
> > aCurveList
= xContainer
->getRegressionCurves();
1317 for(sal_Int32 nN
=0; nN
<aCurveList
.getLength(); nN
++)
1319 uno::Reference
< XRegressionCurveCalculator
> xCalculator( aCurveList
[nN
]->getCalculator() );
1320 if( !xCalculator
.is())
1323 uno::Reference
< beans::XPropertySet
> xProperties( aCurveList
[nN
], uno::UNO_QUERY
);
1325 bool bAverageLine
= RegressionCurveHelper::isMeanValueLine( aCurveList
[nN
] );
1327 sal_Int32 aDegree
= 2;
1328 sal_Int32 aPeriod
= 2;
1329 double aExtrapolateForward
= 0.0;
1330 double aExtrapolateBackward
= 0.0;
1331 bool bForceIntercept
= false;
1332 double aInterceptValue
= 0.0;
1334 if ( xProperties
.is() && !bAverageLine
)
1336 xProperties
->getPropertyValue( "PolynomialDegree") >>= aDegree
;
1337 xProperties
->getPropertyValue( "MovingAveragePeriod") >>= aPeriod
;
1338 xProperties
->getPropertyValue( "ExtrapolateForward") >>= aExtrapolateForward
;
1339 xProperties
->getPropertyValue( "ExtrapolateBackward") >>= aExtrapolateBackward
;
1340 xProperties
->getPropertyValue( "ForceIntercept") >>= bForceIntercept
;
1341 if (bForceIntercept
)
1342 xProperties
->getPropertyValue( "InterceptValue") >>= aInterceptValue
;
1345 double fChartMinX
= m_pPosHelper
->getLogicMinX();
1346 double fChartMaxX
= m_pPosHelper
->getLogicMaxX();
1348 double fMinX
= fChartMinX
;
1349 double fMaxX
= fChartMaxX
;
1351 double fPointScale
= 1.0;
1355 rVDataSeries
.getMinMaxXValue(fMinX
, fMaxX
);
1356 fMaxX
+= aExtrapolateForward
;
1357 fMinX
-= aExtrapolateBackward
;
1359 fPointScale
= (fMaxX
- fMinX
) / (fChartMaxX
- fChartMinX
);
1360 // sanitize the value, tdf#119922
1361 fPointScale
= std::min(fPointScale
, 1000.0);
1364 xCalculator
->setRegressionProperties(aDegree
, bForceIntercept
, aInterceptValue
, aPeriod
);
1365 xCalculator
->recalculateRegression( rVDataSeries
.getAllX(), rVDataSeries
.getAllY() );
1366 sal_Int32 nPointCount
= 100 * fPointScale
;
1368 if ( nPointCount
< 2 )
1371 std::vector
< ExplicitScaleData
> aScales( m_pPosHelper
->getScales());
1372 uno::Reference
< chart2::XScaling
> xScalingX
;
1373 uno::Reference
< chart2::XScaling
> xScalingY
;
1374 if( aScales
.size() >= 2 )
1376 xScalingX
.set( aScales
[0].Scaling
);
1377 xScalingY
.set( aScales
[1].Scaling
);
1380 const uno::Sequence
< geometry::RealPoint2D
> aCalculatedPoints(
1381 xCalculator
->getCurveValues(
1382 fMinX
, fMaxX
, nPointCount
,
1383 xScalingX
, xScalingY
, bMaySkipPoints
));
1385 nPointCount
= aCalculatedPoints
.getLength();
1387 drawing::PolyPolygonShape3D aRegressionPoly
;
1388 aRegressionPoly
.SequenceX
.realloc(1);
1389 aRegressionPoly
.SequenceY
.realloc(1);
1390 aRegressionPoly
.SequenceZ
.realloc(1);
1391 aRegressionPoly
.SequenceX
[0].realloc(nPointCount
);
1392 aRegressionPoly
.SequenceY
[0].realloc(nPointCount
);
1393 aRegressionPoly
.SequenceZ
[0].realloc(nPointCount
);
1395 sal_Int32 nRealPointCount
= 0;
1397 for(geometry::RealPoint2D
const & p
: aCalculatedPoints
)
1399 double fLogicX
= p
.X
;
1400 double fLogicY
= p
.Y
;
1401 double fLogicZ
= 0.0; //dummy
1403 // fdo#51656: don't scale mean value lines
1405 m_pPosHelper
->doLogicScaling( &fLogicX
, &fLogicY
, &fLogicZ
);
1407 if(!std::isnan(fLogicX
) && !std::isinf(fLogicX
) &&
1408 !std::isnan(fLogicY
) && !std::isinf(fLogicY
) &&
1409 !std::isnan(fLogicZ
) && !std::isinf(fLogicZ
) )
1411 aRegressionPoly
.SequenceX
[0][nRealPointCount
] = fLogicX
;
1412 aRegressionPoly
.SequenceY
[0][nRealPointCount
] = fLogicY
;
1416 aRegressionPoly
.SequenceX
[0].realloc(nRealPointCount
);
1417 aRegressionPoly
.SequenceY
[0].realloc(nRealPointCount
);
1418 aRegressionPoly
.SequenceZ
[0].realloc(nRealPointCount
);
1420 drawing::PolyPolygonShape3D aClippedPoly
;
1421 Clipping::clipPolygonAtRectangle( aRegressionPoly
, m_pPosHelper
->getScaledLogicClipDoubleRect(), aClippedPoly
);
1422 aRegressionPoly
= aClippedPoly
;
1423 m_pPosHelper
->transformScaledLogicToScene( aRegressionPoly
);
1425 awt::Point aDefaultPos
;
1426 if( aRegressionPoly
.SequenceX
.hasElements() && aRegressionPoly
.SequenceX
[0].hasElements() )
1428 VLineProperties aVLineProperties
;
1429 aVLineProperties
.initFromPropertySet( xProperties
);
1431 //create an extra group shape for each curve for selection handling
1432 uno::Reference
< drawing::XShapes
> xRegressionGroupShapes
=
1433 createGroupShape( xTarget
, rVDataSeries
.getDataCurveCID( nN
, bAverageLine
) );
1434 uno::Reference
< drawing::XShape
> xShape
= m_pShapeFactory
->createLine2D(
1435 xRegressionGroupShapes
, PolyToPointSequence( aRegressionPoly
), &aVLineProperties
);
1436 ShapeFactory::setShapeName( xShape
, "MarkHandles" );
1437 aDefaultPos
= xShape
->getPosition();
1440 // curve equation and correlation coefficient
1441 uno::Reference
< beans::XPropertySet
> xEquationProperties( aCurveList
[nN
]->getEquationProperties());
1442 if( xEquationProperties
.is())
1444 createRegressionCurveEquationShapes(
1445 rVDataSeries
.getDataCurveEquationCID( nN
),
1446 xEquationProperties
, xEquationTarget
, xCalculator
,
1452 static sal_Int32
lcl_getOUStringMaxLineLength ( OUStringBuffer
const & aString
)
1454 const sal_Int32 nStringLength
= aString
.getLength();
1455 sal_Int32 nMaxLineLength
= 0;
1457 for ( sal_Int32 i
=0; i
<nStringLength
; i
++ )
1459 sal_Int32 indexSep
= aString
.indexOf( "\n", i
);
1461 indexSep
= nStringLength
;
1462 sal_Int32 nLineLength
= indexSep
- i
;
1463 if ( nLineLength
> nMaxLineLength
)
1464 nMaxLineLength
= nLineLength
;
1468 return nMaxLineLength
;
1471 void VSeriesPlotter::createRegressionCurveEquationShapes(
1472 const OUString
& rEquationCID
,
1473 const uno::Reference
< beans::XPropertySet
> & xEquationProperties
,
1474 const uno::Reference
< drawing::XShapes
>& xEquationTarget
,
1475 const uno::Reference
< chart2::XRegressionCurveCalculator
> & xRegressionCurveCalculator
,
1476 awt::Point aDefaultPos
)
1478 OSL_ASSERT( xEquationProperties
.is());
1479 if( !xEquationProperties
.is())
1482 bool bShowEquation
= false;
1483 bool bShowCorrCoeff
= false;
1484 if(!(( xEquationProperties
->getPropertyValue( "ShowEquation") >>= bShowEquation
) &&
1485 ( xEquationProperties
->getPropertyValue( "ShowCorrelationCoefficient") >>= bShowCorrCoeff
)))
1488 if( ! (bShowEquation
|| bShowCorrCoeff
))
1491 OUStringBuffer aFormula
;
1492 sal_Int32 nNumberFormatKey
= 0;
1493 sal_Int32 nFormulaWidth
= 0;
1494 xEquationProperties
->getPropertyValue(CHART_UNONAME_NUMFMT
) >>= nNumberFormatKey
;
1495 bool bResizeEquation
= true;
1496 sal_Int32 nMaxIteration
= 2;
1497 if ( bShowEquation
)
1499 OUString aXName
, aYName
;
1500 if ( !(xEquationProperties
->getPropertyValue( "XName" ) >>= aXName
) )
1501 aXName
= OUString( "x" );
1502 if ( !(xEquationProperties
->getPropertyValue( "YName" ) >>= aYName
) )
1503 aYName
= OUString( "f(x)" );
1504 xRegressionCurveCalculator
->setXYNames( aXName
, aYName
);
1507 for ( sal_Int32 nCountIteration
= 0; bResizeEquation
&& nCountIteration
< nMaxIteration
; nCountIteration
++ )
1509 bResizeEquation
= false;
1512 if (m_apNumberFormatterWrapper
)
1513 { // iteration 0: default representation (no wrap)
1514 // iteration 1: expected width (nFormulaWidth) is calculated
1515 aFormula
= xRegressionCurveCalculator
->getFormattedRepresentation(
1516 m_apNumberFormatterWrapper
->getNumberFormatsSupplier(),
1517 nNumberFormatKey
, nFormulaWidth
);
1518 nFormulaWidth
= lcl_getOUStringMaxLineLength( aFormula
);
1522 aFormula
= xRegressionCurveCalculator
->getRepresentation();
1525 if( bShowCorrCoeff
)
1527 aFormula
.append( "\n" );
1530 if( bShowCorrCoeff
)
1532 aFormula
.append( "R" ).append( OUStringChar( aSuperscriptFigures
[2] ) ).append( " = " );
1533 double fR( xRegressionCurveCalculator
->getCorrelationCoefficient());
1534 if (m_apNumberFormatterWrapper
)
1539 m_apNumberFormatterWrapper
->getFormattedString(
1540 nNumberFormatKey
, fR
*fR
, nLabelCol
, bColChanged
));
1541 //@todo: change color of label if bColChanged is true
1545 const LocaleDataWrapper
& rLocaleDataWrapper
= Application::GetSettings().GetLocaleDataWrapper();
1546 const OUString
& aNumDecimalSep
= rLocaleDataWrapper
.getNumDecimalSep();
1547 sal_Unicode aDecimalSep
= aNumDecimalSep
[0];
1548 aFormula
.append( ::rtl::math::doubleToUString(
1549 fR
*fR
, rtl_math_StringFormat_G
, 4, aDecimalSep
, true ));
1553 awt::Point aScreenPosition2D
;
1554 chart2::RelativePosition aRelativePosition
;
1555 if( xEquationProperties
->getPropertyValue( "RelativePosition") >>= aRelativePosition
)
1557 //@todo decide whether x is primary or secondary
1558 double fX
= aRelativePosition
.Primary
*m_aPageReferenceSize
.Width
;
1559 double fY
= aRelativePosition
.Secondary
*m_aPageReferenceSize
.Height
;
1560 aScreenPosition2D
.X
= static_cast< sal_Int32
>( ::rtl::math::round( fX
));
1561 aScreenPosition2D
.Y
= static_cast< sal_Int32
>( ::rtl::math::round( fY
));
1564 aScreenPosition2D
= aDefaultPos
;
1566 if( !aFormula
.isEmpty())
1568 // set fill and line properties on creation
1569 tNameSequence aNames
;
1570 tAnySequence aValues
;
1571 PropertyMapper::getPreparedTextShapePropertyLists( xEquationProperties
, aNames
, aValues
);
1573 uno::Reference
< drawing::XShape
> xTextShape
= m_pShapeFactory
->createText(
1574 xEquationTarget
, aFormula
.makeStringAndClear(),
1575 aNames
, aValues
, ShapeFactory::makeTransformation( aScreenPosition2D
));
1577 OSL_ASSERT( xTextShape
.is());
1578 if( xTextShape
.is())
1580 ShapeFactory::setShapeName( xTextShape
, rEquationCID
);
1581 awt::Size
aSize( xTextShape
->getSize() );
1582 awt::Point
aPos( RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
1583 aScreenPosition2D
, aSize
, aRelativePosition
.Anchor
) );
1584 //ensure that the equation is fully placed within the page (if possible)
1585 if( (aPos
.X
+ aSize
.Width
) > m_aPageReferenceSize
.Width
)
1586 aPos
.X
= m_aPageReferenceSize
.Width
- aSize
.Width
;
1590 if ( nFormulaWidth
> 0 )
1592 bResizeEquation
= true;
1593 if ( nCountIteration
< nMaxIteration
-1 )
1594 xEquationTarget
->remove( xTextShape
); // remove equation
1595 nFormulaWidth
*= m_aPageReferenceSize
.Width
/ static_cast< double >(aSize
.Width
);
1596 nFormulaWidth
-= nCountIteration
;
1597 if ( nFormulaWidth
< 0 )
1601 if( (aPos
.Y
+ aSize
.Height
) > m_aPageReferenceSize
.Height
)
1602 aPos
.Y
= m_aPageReferenceSize
.Height
- aSize
.Height
;
1605 if ( !bResizeEquation
|| nCountIteration
== nMaxIteration
-1 )
1606 xTextShape
->setPosition(aPos
); // if equation was not removed
1612 void VSeriesPlotter::setMappedProperties(
1613 const uno::Reference
< drawing::XShape
>& xTargetShape
1614 , const uno::Reference
< beans::XPropertySet
>& xSource
1615 , const tPropertyNameMap
& rMap
1616 , tPropertyNameValueMap
const * pOverwriteMap
)
1618 uno::Reference
< beans::XPropertySet
> xTargetProp( xTargetShape
, uno::UNO_QUERY
);
1619 PropertyMapper::setMappedProperties(xTargetProp
,xSource
,rMap
,pOverwriteMap
);
1622 void VSeriesPlotter::setTimeResolutionOnXAxis( tools::Long TimeResolution
, const Date
& rNullDate
)
1624 m_nTimeResolution
= TimeResolution
;
1625 m_aNullDate
= rNullDate
;
1628 // MinimumAndMaximumSupplier
1629 tools::Long
VSeriesPlotter::calculateTimeResolutionOnXAxis()
1631 tools::Long nRet
= css::chart::TimeUnit::YEAR
;
1632 if (!m_pExplicitCategoriesProvider
)
1635 const std::vector
<double>& rDateCategories
= m_pExplicitCategoriesProvider
->getDateCategories();
1636 if (rDateCategories
.empty())
1639 std::vector
<double>::const_iterator aIt
= rDateCategories
.begin(), aEnd
= rDateCategories
.end();
1641 aIt
= std::find_if(aIt
, aEnd
, [](const double& rDateCategory
) { return !std::isnan(rDateCategory
); });
1645 Date
aNullDate(30,12,1899);
1646 if (m_apNumberFormatterWrapper
)
1647 aNullDate
= m_apNumberFormatterWrapper
->getNullDate();
1649 Date
aPrevious(aNullDate
); aPrevious
.AddDays(rtl::math::approxFloor(*aIt
));
1651 for(;aIt
!=aEnd
;++aIt
)
1653 if (std::isnan(*aIt
))
1656 Date
aCurrent(aNullDate
); aCurrent
.AddDays(rtl::math::approxFloor(*aIt
));
1657 if( nRet
== css::chart::TimeUnit::YEAR
)
1659 if( DateHelper::IsInSameYear( aPrevious
, aCurrent
) )
1660 nRet
= css::chart::TimeUnit::MONTH
;
1662 if( nRet
== css::chart::TimeUnit::MONTH
)
1664 if( DateHelper::IsInSameMonth( aPrevious
, aCurrent
) )
1665 nRet
= css::chart::TimeUnit::DAY
;
1667 if( nRet
== css::chart::TimeUnit::DAY
)
1674 double VSeriesPlotter::getMinimumX()
1676 double fMinimum
, fMaximum
;
1677 getMinimumAndMaximumX( fMinimum
, fMaximum
);
1680 double VSeriesPlotter::getMaximumX()
1682 double fMinimum
, fMaximum
;
1683 getMinimumAndMaximumX( fMinimum
, fMaximum
);
1687 double VSeriesPlotter::getMinimumYInRange( double fMinimumX
, double fMaximumX
, sal_Int32 nAxisIndex
)
1689 if( !m_bCategoryXAxis
|| ( m_pExplicitCategoriesProvider
&& m_pExplicitCategoriesProvider
->isDateAxis() ) )
1691 double fMinY
, fMaxY
;
1692 getMinimumAndMaximumYInContinuousXRange( fMinY
, fMaxY
, fMinimumX
, fMaximumX
, nAxisIndex
);
1696 double fMinimum
, fMaximum
;
1697 ::rtl::math::setInf(&fMinimum
, false);
1698 ::rtl::math::setInf(&fMaximum
, true);
1699 for(std::vector
<VDataSeriesGroup
> & rXSlots
: m_aZSlots
)
1701 for(VDataSeriesGroup
& rXSlot
: rXSlots
)
1703 double fLocalMinimum
, fLocalMaximum
;
1704 rXSlot
.calculateYMinAndMaxForCategoryRange(
1705 static_cast<sal_Int32
>(fMinimumX
-1.0) //first category (index 0) matches with real number 1.0
1706 , static_cast<sal_Int32
>(fMaximumX
-1.0) //first category (index 0) matches with real number 1.0
1707 , isSeparateStackingForDifferentSigns( 1 )
1708 , fLocalMinimum
, fLocalMaximum
, nAxisIndex
);
1709 if(fMaximum
<fLocalMaximum
)
1710 fMaximum
=fLocalMaximum
;
1711 if(fMinimum
>fLocalMinimum
)
1712 fMinimum
=fLocalMinimum
;
1715 if(std::isinf(fMinimum
))
1716 ::rtl::math::setNan(&fMinimum
);
1720 double VSeriesPlotter::getMaximumYInRange( double fMinimumX
, double fMaximumX
, sal_Int32 nAxisIndex
)
1722 if( !m_bCategoryXAxis
|| ( m_pExplicitCategoriesProvider
&& m_pExplicitCategoriesProvider
->isDateAxis() ) )
1724 double fMinY
, fMaxY
;
1725 getMinimumAndMaximumYInContinuousXRange( fMinY
, fMaxY
, fMinimumX
, fMaximumX
, nAxisIndex
);
1729 double fMinimum
, fMaximum
;
1730 ::rtl::math::setInf(&fMinimum
, false);
1731 ::rtl::math::setInf(&fMaximum
, true);
1732 for( std::vector
< VDataSeriesGroup
> & rXSlots
: m_aZSlots
)
1734 for(VDataSeriesGroup
& rXSlot
: rXSlots
)
1736 double fLocalMinimum
, fLocalMaximum
;
1737 rXSlot
.calculateYMinAndMaxForCategoryRange(
1738 static_cast<sal_Int32
>(fMinimumX
-1.0) //first category (index 0) matches with real number 1.0
1739 , static_cast<sal_Int32
>(fMaximumX
-1.0) //first category (index 0) matches with real number 1.0
1740 , isSeparateStackingForDifferentSigns( 1 )
1741 , fLocalMinimum
, fLocalMaximum
, nAxisIndex
);
1742 if(fMaximum
<fLocalMaximum
)
1743 fMaximum
=fLocalMaximum
;
1744 if(fMinimum
>fLocalMinimum
)
1745 fMinimum
=fLocalMinimum
;
1748 if(std::isinf(fMaximum
))
1749 ::rtl::math::setNan(&fMaximum
);
1753 double VSeriesPlotter::getMinimumZ()
1755 //this is the default for all charts without a meaningful z axis
1758 double VSeriesPlotter::getMaximumZ()
1760 if( m_nDimension
!=3 || m_aZSlots
.empty() )
1761 return getMinimumZ()+1;
1762 return m_aZSlots
.size();
1767 bool lcl_isValueAxis( sal_Int32 nDimensionIndex
, bool bCategoryXAxis
)
1769 // default implementation: true for Y axes, and for value X axis
1770 if( nDimensionIndex
== 0 )
1771 return !bCategoryXAxis
;
1772 return nDimensionIndex
== 1;
1776 bool VSeriesPlotter::isExpandBorderToIncrementRhythm( sal_Int32 nDimensionIndex
)
1778 return lcl_isValueAxis( nDimensionIndex
, m_bCategoryXAxis
);
1781 bool VSeriesPlotter::isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex
)
1783 // do not expand axes in 3D charts
1784 return (m_nDimension
< 3) && lcl_isValueAxis( nDimensionIndex
, m_bCategoryXAxis
);
1787 bool VSeriesPlotter::isExpandWideValuesToZero( sal_Int32 nDimensionIndex
)
1789 // default implementation: only for Y axis
1790 return nDimensionIndex
== 1;
1793 bool VSeriesPlotter::isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex
)
1795 // default implementation: only for Y axis
1796 return nDimensionIndex
== 1;
1799 bool VSeriesPlotter::isSeparateStackingForDifferentSigns( sal_Int32 nDimensionIndex
)
1801 // default implementation: only for Y axis
1802 return nDimensionIndex
== 1;
1805 void VSeriesPlotter::getMinimumAndMaximumX( double& rfMinimum
, double& rfMaximum
) const
1807 ::rtl::math::setInf(&rfMinimum
, false);
1808 ::rtl::math::setInf(&rfMaximum
, true);
1810 for (auto const& ZSlot
: m_aZSlots
)
1812 for (auto const& XSlot
: ZSlot
)
1814 double fLocalMinimum
, fLocalMaximum
;
1815 XSlot
.getMinimumAndMaximumX( fLocalMinimum
, fLocalMaximum
);
1816 if( !std::isnan(fLocalMinimum
) && fLocalMinimum
< rfMinimum
)
1817 rfMinimum
= fLocalMinimum
;
1818 if( !std::isnan(fLocalMaximum
) && fLocalMaximum
> rfMaximum
)
1819 rfMaximum
= fLocalMaximum
;
1822 if(std::isinf(rfMinimum
))
1823 ::rtl::math::setNan(&rfMinimum
);
1824 if(std::isinf(rfMaximum
))
1825 ::rtl::math::setNan(&rfMaximum
);
1828 void VSeriesPlotter::getMinimumAndMaximumYInContinuousXRange( double& rfMinY
, double& rfMaxY
, double fMinX
, double fMaxX
, sal_Int32 nAxisIndex
) const
1830 ::rtl::math::setInf(&rfMinY
, false);
1831 ::rtl::math::setInf(&rfMaxY
, true);
1833 for (auto const& ZSlot
: m_aZSlots
)
1835 for (auto const& XSlot
: ZSlot
)
1837 double fLocalMinimum
, fLocalMaximum
;
1838 XSlot
.getMinimumAndMaximumYInContinuousXRange( fLocalMinimum
, fLocalMaximum
, fMinX
, fMaxX
, nAxisIndex
);
1839 if( !std::isnan(fLocalMinimum
) && fLocalMinimum
< rfMinY
)
1840 rfMinY
= fLocalMinimum
;
1841 if( !std::isnan(fLocalMaximum
) && fLocalMaximum
> rfMaxY
)
1842 rfMaxY
= fLocalMaximum
;
1845 if(std::isinf(rfMinY
))
1846 ::rtl::math::setNan(&rfMinY
);
1847 if(std::isinf(rfMaxY
))
1848 ::rtl::math::setNan(&rfMaxY
);
1851 sal_Int32
VSeriesPlotter::getPointCount() const
1855 for (auto const& ZSlot
: m_aZSlots
)
1857 for (auto const& XSlot
: ZSlot
)
1859 sal_Int32 nPointCount
= XSlot
.getPointCount();
1860 if( nPointCount
>nRet
)
1867 void VSeriesPlotter::setNumberFormatsSupplier(
1868 const uno::Reference
< util::XNumberFormatsSupplier
> & xNumFmtSupplier
)
1870 m_apNumberFormatterWrapper
.reset( new NumberFormatterWrapper( xNumFmtSupplier
));
1873 void VSeriesPlotter::setColorScheme( const uno::Reference
< XColorScheme
>& xColorScheme
)
1875 m_xColorScheme
= xColorScheme
;
1878 void VSeriesPlotter::setExplicitCategoriesProvider( ExplicitCategoriesProvider
* pExplicitCategoriesProvider
)
1880 m_pExplicitCategoriesProvider
= pExplicitCategoriesProvider
;
1883 sal_Int32
VDataSeriesGroup::getPointCount() const
1885 if(!m_bMaxPointCountDirty
)
1886 return m_nMaxPointCount
;
1890 for (std::unique_ptr
<VDataSeries
> const & pSeries
: m_aSeriesVector
)
1892 sal_Int32 nPointCount
= pSeries
->getTotalPointCount();
1893 if( nPointCount
>nRet
)
1896 m_nMaxPointCount
=nRet
;
1897 m_aListOfCachedYValues
.clear();
1898 m_aListOfCachedYValues
.resize(m_nMaxPointCount
);
1899 m_bMaxPointCountDirty
=false;
1903 sal_Int32
VDataSeriesGroup::getAttachedAxisIndexForFirstSeries() const
1907 if (!m_aSeriesVector
.empty())
1908 nRet
= m_aSeriesVector
[0]->getAttachedAxisIndex();
1913 void VDataSeriesGroup::getMinimumAndMaximumX( double& rfMinimum
, double& rfMaximum
) const
1916 ::rtl::math::setInf(&rfMinimum
, false);
1917 ::rtl::math::setInf(&rfMaximum
, true);
1919 for (std::unique_ptr
<VDataSeries
> const & pSeries
: m_aSeriesVector
)
1921 sal_Int32 nPointCount
= pSeries
->getTotalPointCount();
1922 for(sal_Int32 nN
=0;nN
<nPointCount
;nN
++)
1924 double fX
= pSeries
->getXValue( nN
);
1925 if( std::isnan(fX
) )
1933 if(std::isinf(rfMinimum
))
1934 ::rtl::math::setNan(&rfMinimum
);
1935 if(std::isinf(rfMaximum
))
1936 ::rtl::math::setNan(&rfMaximum
);
1942 * Keep track of minimum and maximum Y values for one or more data series.
1943 * When multiple data series exist, that indicates that the data series are
1946 * <p>For each X value, we calculate separate Y value ranges for each data
1947 * series in the first pass. In the second pass, we calculate the minimum Y
1948 * value by taking the absolute minimum value of all data series, whereas
1949 * the maximum Y value is the sum of all the series maximum Y values.</p>
1951 * <p>Once that's done for all X values, the final min / max Y values get
1952 * calculated by taking the absolute min / max Y values across all the X
1955 class PerXMinMaxCalculator
1957 typedef std::pair
<double, double> MinMaxType
;
1958 typedef std::map
<size_t, MinMaxType
> SeriesMinMaxType
;
1959 typedef std::map
<double, std::unique_ptr
<SeriesMinMaxType
>> GroupMinMaxType
;
1960 typedef std::unordered_map
<double, MinMaxType
> TotalStoreType
;
1961 GroupMinMaxType m_SeriesGroup
;
1965 PerXMinMaxCalculator() : mnCurSeries(0) {}
1967 void nextSeries() { ++mnCurSeries
; }
1969 void setValue(double fX
, double fY
)
1971 SeriesMinMaxType
* pStore
= getByXValue(fX
); // get storage for given X value.
1973 // This shouldn't happen!
1976 SeriesMinMaxType::iterator it
= pStore
->lower_bound(mnCurSeries
);
1977 if (it
!= pStore
->end() && !pStore
->key_comp()(mnCurSeries
, it
->first
))
1979 MinMaxType
& r
= it
->second
;
1980 // A min-max pair already exists for this series. Update it.
1988 // No existing pair. Insert a new one.
1990 it
, SeriesMinMaxType::value_type(
1991 mnCurSeries
, MinMaxType(fY
,fY
)));
1995 void getTotalRange(double& rfMin
, double& rfMax
) const
1997 rtl::math::setNan(&rfMin
);
1998 rtl::math::setNan(&rfMax
);
2000 TotalStoreType aStore
;
2001 getTotalStore(aStore
);
2006 TotalStoreType::const_iterator it
= aStore
.begin(), itEnd
= aStore
.end();
2007 rfMin
= it
->second
.first
;
2008 rfMax
= it
->second
.second
;
2009 for (++it
; it
!= itEnd
; ++it
)
2011 if (rfMin
> it
->second
.first
)
2012 rfMin
= it
->second
.first
;
2013 if (rfMax
< it
->second
.second
)
2014 rfMax
= it
->second
.second
;
2020 * Parse all data and reduce them into a set of global Y value ranges per
2023 void getTotalStore(TotalStoreType
& rStore
) const
2025 TotalStoreType aStore
;
2026 for (auto const& it
: m_SeriesGroup
)
2028 double fX
= it
.first
;
2030 const SeriesMinMaxType
& rSeries
= *it
.second
;
2031 for (auto const& series
: rSeries
)
2033 double fYMin
= series
.second
.first
, fYMax
= series
.second
.second
;
2034 TotalStoreType::iterator itr
= aStore
.find(fX
);
2035 if (itr
== aStore
.end())
2036 // New min-max pair for give X value.
2037 aStore
.emplace(fX
, std::pair
<double,double>(fYMin
,fYMax
));
2040 MinMaxType
& r
= itr
->second
;
2041 if (fYMin
< r
.first
)
2042 r
.first
= fYMin
; // min y-value
2044 r
.second
+= fYMax
; // accumulative max y-value.
2048 rStore
.swap(aStore
);
2051 SeriesMinMaxType
* getByXValue(double fX
)
2053 GroupMinMaxType::iterator it
= m_SeriesGroup
.find(fX
);
2054 if (it
== m_SeriesGroup
.end())
2056 std::pair
<GroupMinMaxType::iterator
,bool> r
=
2057 m_SeriesGroup
.insert(std::make_pair(fX
, std::make_unique
<SeriesMinMaxType
>()));
2060 // insertion failed.
2066 return it
->second
.get();
2072 void VDataSeriesGroup::getMinimumAndMaximumYInContinuousXRange(
2073 double& rfMinY
, double& rfMaxY
, double fMinX
, double fMaxX
, sal_Int32 nAxisIndex
) const
2075 ::rtl::math::setNan(&rfMinY
);
2076 ::rtl::math::setNan(&rfMaxY
);
2078 if (m_aSeriesVector
.empty())
2079 // No data series. Bail out.
2082 PerXMinMaxCalculator aRangeCalc
;
2083 for (const std::unique_ptr
<VDataSeries
> & pSeries
: m_aSeriesVector
)
2088 for (sal_Int32 i
= 0, n
= pSeries
->getTotalPointCount(); i
< n
; ++i
)
2090 if (nAxisIndex
!= pSeries
->getAttachedAxisIndex())
2093 double fX
= pSeries
->getXValue(i
);
2097 if (fX
< fMinX
|| fX
> fMaxX
)
2098 // Outside specified X range. Skip it.
2101 double fY
= pSeries
->getYValue(i
);
2105 aRangeCalc
.setValue(fX
, fY
);
2107 aRangeCalc
.nextSeries();
2110 aRangeCalc
.getTotalRange(rfMinY
, rfMaxY
);
2113 void VDataSeriesGroup::calculateYMinAndMaxForCategory( sal_Int32 nCategoryIndex
2114 , bool bSeparateStackingForDifferentSigns
2115 , double& rfMinimumY
, double& rfMaximumY
, sal_Int32 nAxisIndex
)
2117 assert(nCategoryIndex
>= 0);
2118 assert(nCategoryIndex
< getPointCount());
2120 ::rtl::math::setInf(&rfMinimumY
, false);
2121 ::rtl::math::setInf(&rfMaximumY
, true);
2123 if(m_aSeriesVector
.empty())
2126 CachedYValues aCachedYValues
= m_aListOfCachedYValues
[nCategoryIndex
][nAxisIndex
];
2127 if( !aCachedYValues
.m_bValuesDirty
)
2129 //return cached values
2130 rfMinimumY
= aCachedYValues
.m_fMinimumY
;
2131 rfMaximumY
= aCachedYValues
.m_fMaximumY
;
2135 double fTotalSum
, fPositiveSum
, fNegativeSum
, fFirstPositiveY
, fFirstNegativeY
;
2136 ::rtl::math::setNan( &fTotalSum
);
2137 ::rtl::math::setNan( &fPositiveSum
);
2138 ::rtl::math::setNan( &fNegativeSum
);
2139 ::rtl::math::setNan( &fFirstPositiveY
);
2140 ::rtl::math::setNan( &fFirstNegativeY
);
2142 if( bSeparateStackingForDifferentSigns
)
2144 for (const std::unique_ptr
<VDataSeries
> & pSeries
: m_aSeriesVector
)
2146 if( nAxisIndex
!= pSeries
->getAttachedAxisIndex() )
2149 double fValueMinY
= pSeries
->getMinimumofAllDifferentYValues( nCategoryIndex
);
2150 double fValueMaxY
= pSeries
->getMaximumofAllDifferentYValues( nCategoryIndex
);
2152 if( fValueMaxY
>= 0 )
2154 if( std::isnan( fPositiveSum
) )
2155 fPositiveSum
= fFirstPositiveY
= fValueMaxY
;
2157 fPositiveSum
+= fValueMaxY
;
2159 if( fValueMinY
< 0 )
2161 if(std::isnan( fNegativeSum
))
2162 fNegativeSum
= fFirstNegativeY
= fValueMinY
;
2164 fNegativeSum
+= fValueMinY
;
2167 rfMinimumY
= std::isnan( fNegativeSum
) ? fFirstPositiveY
: fNegativeSum
;
2168 rfMaximumY
= std::isnan( fPositiveSum
) ? fFirstNegativeY
: fPositiveSum
;
2172 for (const std::unique_ptr
<VDataSeries
> & pSeries
: m_aSeriesVector
)
2174 if( nAxisIndex
!= pSeries
->getAttachedAxisIndex() )
2177 double fValueMinY
= pSeries
->getMinimumofAllDifferentYValues( nCategoryIndex
);
2178 double fValueMaxY
= pSeries
->getMaximumofAllDifferentYValues( nCategoryIndex
);
2180 if( std::isnan( fTotalSum
) )
2182 rfMinimumY
= fValueMinY
;
2183 rfMaximumY
= fTotalSum
= fValueMaxY
;
2187 fTotalSum
+= fValueMaxY
;
2188 if( rfMinimumY
> fTotalSum
)
2189 rfMinimumY
= fTotalSum
;
2190 if( rfMaximumY
< fTotalSum
)
2191 rfMaximumY
= fTotalSum
;
2196 aCachedYValues
.m_fMinimumY
= rfMinimumY
;
2197 aCachedYValues
.m_fMaximumY
= rfMaximumY
;
2198 aCachedYValues
.m_bValuesDirty
= false;
2199 m_aListOfCachedYValues
[nCategoryIndex
][nAxisIndex
]=aCachedYValues
;
2202 void VDataSeriesGroup::calculateYMinAndMaxForCategoryRange(
2203 sal_Int32 nStartCategoryIndex
, sal_Int32 nEndCategoryIndex
2204 , bool bSeparateStackingForDifferentSigns
2205 , double& rfMinimumY
, double& rfMaximumY
, sal_Int32 nAxisIndex
)
2207 //@todo maybe cache these values
2208 ::rtl::math::setInf(&rfMinimumY
, false);
2209 ::rtl::math::setInf(&rfMaximumY
, true);
2211 //iterate through the given categories
2212 if(nStartCategoryIndex
<0)
2213 nStartCategoryIndex
=0;
2214 const sal_Int32 nPointCount
= getPointCount();//necessary to create m_aListOfCachedYValues
2215 if(nPointCount
<= 0)
2217 if (nEndCategoryIndex
>= nPointCount
)
2218 nEndCategoryIndex
= nPointCount
- 1;
2219 if(nEndCategoryIndex
<0)
2220 nEndCategoryIndex
=0;
2221 for( sal_Int32 nCatIndex
= nStartCategoryIndex
; nCatIndex
<= nEndCategoryIndex
; nCatIndex
++ )
2223 double fMinimumY
; ::rtl::math::setNan(&fMinimumY
);
2224 double fMaximumY
; ::rtl::math::setNan(&fMaximumY
);
2226 calculateYMinAndMaxForCategory( nCatIndex
2227 , bSeparateStackingForDifferentSigns
, fMinimumY
, fMaximumY
, nAxisIndex
);
2229 if(rfMinimumY
> fMinimumY
)
2230 rfMinimumY
= fMinimumY
;
2231 if(rfMaximumY
< fMaximumY
)
2232 rfMaximumY
= fMaximumY
;
2236 double VSeriesPlotter::getTransformedDepth() const
2238 double MinZ
= m_pMainPosHelper
->getLogicMinZ();
2239 double MaxZ
= m_pMainPosHelper
->getLogicMaxZ();
2240 m_pMainPosHelper
->doLogicScaling( nullptr, nullptr, &MinZ
);
2241 m_pMainPosHelper
->doLogicScaling( nullptr, nullptr, &MaxZ
);
2242 return FIXED_SIZE_FOR_3D_CHART_VOLUME
/(MaxZ
-MinZ
);
2245 void VSeriesPlotter::addSecondaryValueScale( const ExplicitScaleData
& rScale
, sal_Int32 nAxisIndex
)
2250 m_aSecondaryValueScales
[nAxisIndex
]=rScale
;
2253 PlottingPositionHelper
& VSeriesPlotter::getPlottingPositionHelper( sal_Int32 nAxisIndex
) const
2255 PlottingPositionHelper
* pRet
= nullptr;
2258 tSecondaryPosHelperMap::const_iterator aPosIt
= m_aSecondaryPosHelperMap
.find( nAxisIndex
);
2259 if( aPosIt
!= m_aSecondaryPosHelperMap
.end() )
2261 pRet
= aPosIt
->second
.get();
2263 else if (m_pPosHelper
)
2265 tSecondaryValueScales::const_iterator aScaleIt
= m_aSecondaryValueScales
.find( nAxisIndex
);
2266 if( aScaleIt
!= m_aSecondaryValueScales
.end() )
2268 m_aSecondaryPosHelperMap
[nAxisIndex
] = m_pPosHelper
->createSecondaryPosHelper( aScaleIt
->second
);
2269 pRet
= m_aSecondaryPosHelperMap
[nAxisIndex
].get();
2274 pRet
= m_pMainPosHelper
;
2276 pRet
->setTimeResolution( m_nTimeResolution
, m_aNullDate
);
2280 void VSeriesPlotter::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size
& /*rPageSize*/ )
2284 VDataSeries
* VSeriesPlotter::getFirstSeries() const
2286 for (std::vector
<VDataSeriesGroup
> const & rGroup
: m_aZSlots
)
2288 if (!rGroup
.empty())
2290 if (!rGroup
[0].m_aSeriesVector
.empty())
2292 VDataSeries
* pSeries
= rGroup
[0].m_aSeriesVector
[0].get();
2301 OUString
VSeriesPlotter::getCategoryName( sal_Int32 nPointIndex
) const
2303 if (m_pExplicitCategoriesProvider
)
2305 Sequence
< OUString
> aCategories(m_pExplicitCategoriesProvider
->getSimpleCategories());
2306 if (nPointIndex
>= 0 && nPointIndex
< aCategories
.getLength())
2308 return aCategories
[nPointIndex
];
2314 uno::Sequence
< OUString
> VSeriesPlotter::getSeriesNames() const
2316 std::vector
<OUString
> aRetVector
;
2319 if( m_xChartTypeModel
.is() )
2320 aRole
= m_xChartTypeModel
->getRoleOfSequenceForSeriesLabel();
2322 for (auto const& rGroup
: m_aZSlots
)
2324 if (!rGroup
.empty())
2326 VDataSeriesGroup
const & rSeriesGroup(rGroup
[0]);
2327 if (!rSeriesGroup
.m_aSeriesVector
.empty())
2329 VDataSeries
const * pSeries
= rSeriesGroup
.m_aSeriesVector
[0].get();
2330 uno::Reference
< XDataSeries
> xSeries( pSeries
? pSeries
->getModel() : nullptr );
2333 OUString
aSeriesName( DataSeriesHelper::getDataSeriesLabel( xSeries
, aRole
) );
2334 aRetVector
.push_back( aSeriesName
);
2339 return comphelper::containerToSequence( aRetVector
);
2342 void VSeriesPlotter::setPageReferenceSize( const css::awt::Size
& rPageRefSize
)
2344 m_aPageReferenceSize
= rPageRefSize
;
2346 // set reference size also at all data series
2348 for (auto const & outer
: m_aZSlots
)
2349 for (VDataSeriesGroup
const & rGroup
: outer
)
2351 for (std::unique_ptr
<VDataSeries
> const & pSeries
: rGroup
.m_aSeriesVector
)
2353 pSeries
->setPageReferenceSize(m_aPageReferenceSize
);
2358 //better performance for big data
2359 void VSeriesPlotter::setCoordinateSystemResolution( const Sequence
< sal_Int32
>& rCoordinateSystemResolution
)
2361 m_aCoordinateSystemResolution
= rCoordinateSystemResolution
;
2364 bool VSeriesPlotter::WantToPlotInFrontOfAxisLine()
2366 return ChartTypeHelper::isSeriesInFrontOfAxisLine( m_xChartTypeModel
);
2369 bool VSeriesPlotter::shouldSnapRectToUsedArea()
2371 return m_nDimension
!= 3;
2374 std::vector
< ViewLegendEntry
> VSeriesPlotter::createLegendEntries(
2375 const awt::Size
& rEntryKeyAspectRatio
2376 , LegendPosition eLegendPosition
2377 , const Reference
< beans::XPropertySet
>& xTextProperties
2378 , const Reference
< drawing::XShapes
>& xTarget
2379 , const Reference
< lang::XMultiServiceFactory
>& xShapeFactory
2380 , const Reference
< uno::XComponentContext
>& xContext
2381 , ChartModel
& rModel
2384 std::vector
< ViewLegendEntry
> aResult
;
2388 uno::Reference
< XCoordinateSystemContainer
> xCooSysCnt( rModel
.getFirstDiagram(), uno::UNO_QUERY
);
2389 Reference
< chart2::XCoordinateSystem
> xCooSys(xCooSysCnt
->getCoordinateSystems()[0]);
2390 Reference
< beans::XPropertySet
> xProp( xCooSys
, uno::UNO_QUERY
);
2391 bool bSwapXAndY
= false;
2395 xProp
->getPropertyValue( "SwapXAndYAxis" ) >>= bSwapXAndY
;
2397 catch( const uno::Exception
& )
2401 //iterate through all series
2402 bool bBreak
= false;
2403 bool bFirstSeries
= true;
2406 for (std::vector
<VDataSeriesGroup
> const & rGroupVector
: m_aZSlots
)
2408 for (VDataSeriesGroup
const & rGroup
: rGroupVector
)
2410 for (std::unique_ptr
<VDataSeries
> const & pSeries
: rGroup
.m_aSeriesVector
)
2415 if (!pSeries
->getPropertiesOfSeries()->getPropertyValue("ShowLegendEntry").get
<sal_Bool
>())
2420 std::vector
<ViewLegendEntry
> aSeriesEntries(
2421 createLegendEntriesForSeries(
2422 rEntryKeyAspectRatio
, *pSeries
, xTextProperties
,
2423 xTarget
, xShapeFactory
, xContext
));
2425 //add series entries to the result now
2427 // use only the first series if VaryColorsByPoint is set for the first series
2428 if (bFirstSeries
&& pSeries
->isVaryColorsByPoint())
2430 bFirstSeries
= false;
2432 // add entries reverse if chart is stacked in y-direction and the legend position is right or left.
2433 // If the legend is top or bottom and we have a stacked bar-chart the normal order
2434 // is the correct one, unless the chart type is horizontal bar-chart.
2435 bool bReverse
= false;
2438 StackingDirection
eStackingDirection( pSeries
->getStackingDirection() );
2439 bReverse
= ( eStackingDirection
!= StackingDirection_Y_STACKING
);
2441 else if ( eLegendPosition
== LegendPosition_LINE_START
|| eLegendPosition
== LegendPosition_LINE_END
)
2443 StackingDirection
eStackingDirection( pSeries
->getStackingDirection() );
2444 bReverse
= ( eStackingDirection
== StackingDirection_Y_STACKING
);
2448 aResult
.insert( aResult
.begin(), aSeriesEntries
.begin(), aSeriesEntries
.end() );
2450 aResult
.insert( aResult
.end(), aSeriesEntries
.begin(), aSeriesEntries
.end() );
2461 std::vector
<VDataSeries
*> VSeriesPlotter::getAllSeries()
2463 std::vector
<VDataSeries
*> aAllSeries
;
2464 for (std::vector
<VDataSeriesGroup
> const & rXSlot
: m_aZSlots
)
2466 for(VDataSeriesGroup
const & rGroup
: rXSlot
)
2468 for (std::unique_ptr
<VDataSeries
> const & p
: rGroup
.m_aSeriesVector
)
2469 aAllSeries
.push_back(p
.get());
2477 bool lcl_HasVisibleLine( const uno::Reference
< beans::XPropertySet
>& xProps
, bool& rbHasDashedLine
)
2479 bool bHasVisibleLine
= false;
2480 rbHasDashedLine
= false;
2481 drawing::LineStyle aLineStyle
= drawing::LineStyle_NONE
;
2482 if( xProps
.is() && ( xProps
->getPropertyValue( "LineStyle") >>= aLineStyle
) )
2484 if( aLineStyle
!= drawing::LineStyle_NONE
)
2485 bHasVisibleLine
= true;
2486 if( aLineStyle
== drawing::LineStyle_DASH
)
2487 rbHasDashedLine
= true;
2489 return bHasVisibleLine
;
2492 bool lcl_HasRegressionCurves( const VDataSeries
& rSeries
, bool& rbHasDashedLine
)
2494 bool bHasRegressionCurves
= false;
2495 Reference
< XRegressionCurveContainer
> xRegrCont( rSeries
.getModel(), uno::UNO_QUERY
);
2498 Sequence
< Reference
< XRegressionCurve
> > aCurves( xRegrCont
->getRegressionCurves() );
2499 sal_Int32 i
= 0, nCount
= aCurves
.getLength();
2500 for( i
=0; i
<nCount
; ++i
)
2502 if( aCurves
[i
].is() )
2504 bHasRegressionCurves
= true;
2505 lcl_HasVisibleLine( uno::Reference
< beans::XPropertySet
>( aCurves
[i
], uno::UNO_QUERY
), rbHasDashedLine
);
2509 return bHasRegressionCurves
;
2512 LegendSymbolStyle
VSeriesPlotter::getLegendSymbolStyle()
2514 return LegendSymbolStyle::Box
;
2517 awt::Size
VSeriesPlotter::getPreferredLegendKeyAspectRatio()
2519 awt::Size
aRet(1000,1000);
2520 if( m_nDimension
==3 )
2523 bool bSeriesAllowsLines
= (getLegendSymbolStyle() == LegendSymbolStyle::Line
);
2524 bool bHasLines
= false;
2525 bool bHasDashedLines
= false;
2526 //iterate through all series
2527 for (VDataSeries
* pSeries
: getAllSeries())
2529 if( bSeriesAllowsLines
)
2531 bool bCurrentDashed
= false;
2532 if( lcl_HasVisibleLine( pSeries
->getPropertiesOfSeries(), bCurrentDashed
) )
2535 if( bCurrentDashed
)
2537 bHasDashedLines
= true;
2542 bool bRegressionHasDashedLines
=false;
2543 if( lcl_HasRegressionCurves( *pSeries
, bRegressionHasDashedLines
) )
2546 if( bRegressionHasDashedLines
)
2548 bHasDashedLines
= true;
2555 if( bHasDashedLines
)
2556 aRet
= awt::Size(1600,-1);
2558 aRet
= awt::Size(800,-1);
2563 uno::Any
VSeriesPlotter::getExplicitSymbol( const VDataSeries
& /*rSeries*/, sal_Int32
/*nPointIndex*/ )
2568 Reference
< drawing::XShape
> VSeriesPlotter::createLegendSymbolForSeries(
2569 const awt::Size
& rEntryKeyAspectRatio
2570 , const VDataSeries
& rSeries
2571 , const Reference
< drawing::XShapes
>& xTarget
2572 , const Reference
< lang::XMultiServiceFactory
>& xShapeFactory
)
2575 LegendSymbolStyle eLegendSymbolStyle
= getLegendSymbolStyle();
2576 uno::Any
aExplicitSymbol( getExplicitSymbol( rSeries
, -1 ) );
2578 VLegendSymbolFactory::PropertyType ePropType
=
2579 VLegendSymbolFactory::PropertyType::FilledSeries
;
2581 // todo: maybe the property-style does not solely depend on the
2582 // legend-symbol type
2583 switch( eLegendSymbolStyle
)
2585 case LegendSymbolStyle::Line
:
2586 ePropType
= VLegendSymbolFactory::PropertyType::LineSeries
;
2591 Reference
< drawing::XShape
> xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio
,
2592 xTarget
, eLegendSymbolStyle
, xShapeFactory
2593 , rSeries
.getPropertiesOfSeries(), ePropType
, aExplicitSymbol
));
2598 Reference
< drawing::XShape
> VSeriesPlotter::createLegendSymbolForPoint(
2599 const awt::Size
& rEntryKeyAspectRatio
2600 , const VDataSeries
& rSeries
2601 , sal_Int32 nPointIndex
2602 , const Reference
< drawing::XShapes
>& xTarget
2603 , const Reference
< lang::XMultiServiceFactory
>& xShapeFactory
)
2606 LegendSymbolStyle eLegendSymbolStyle
= getLegendSymbolStyle();
2607 uno::Any
aExplicitSymbol( getExplicitSymbol(rSeries
,nPointIndex
) );
2609 VLegendSymbolFactory::PropertyType ePropType
=
2610 VLegendSymbolFactory::PropertyType::FilledSeries
;
2612 // todo: maybe the property-style does not solely depend on the
2613 // legend-symbol type
2614 switch( eLegendSymbolStyle
)
2616 case LegendSymbolStyle::Line
:
2617 ePropType
= VLegendSymbolFactory::PropertyType::LineSeries
;
2623 // the default properties for the data point are the data series properties.
2624 // If a data point has own attributes overwrite them
2625 Reference
< beans::XPropertySet
> xSeriesProps( rSeries
.getPropertiesOfSeries() );
2626 Reference
< beans::XPropertySet
> xPointSet( xSeriesProps
);
2627 if( rSeries
.isAttributedDataPoint( nPointIndex
) )
2628 xPointSet
.set( rSeries
.getPropertiesOfPoint( nPointIndex
));
2630 // if a data point has no own color use a color from the diagram's color scheme
2631 if( ! rSeries
.hasPointOwnColor( nPointIndex
))
2633 Reference
< util::XCloneable
> xCloneable( xPointSet
,uno::UNO_QUERY
);
2634 if( xCloneable
.is() && m_xColorScheme
.is() )
2636 xPointSet
.set( xCloneable
->createClone(), uno::UNO_QUERY
);
2637 Reference
< container::XChild
> xChild( xPointSet
, uno::UNO_QUERY
);
2639 xChild
->setParent( xSeriesProps
);
2641 OSL_ASSERT( xPointSet
.is());
2642 xPointSet
->setPropertyValue(
2643 "Color", uno::Any( m_xColorScheme
->getColorByIndex( nPointIndex
)));
2647 Reference
< drawing::XShape
> xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio
,
2648 xTarget
, eLegendSymbolStyle
, xShapeFactory
, xPointSet
, ePropType
, aExplicitSymbol
));
2653 std::vector
< ViewLegendEntry
> VSeriesPlotter::createLegendEntriesForSeries(
2654 const awt::Size
& rEntryKeyAspectRatio
2655 , const VDataSeries
& rSeries
2656 , const Reference
< beans::XPropertySet
>& xTextProperties
2657 , const Reference
< drawing::XShapes
>& xTarget
2658 , const Reference
< lang::XMultiServiceFactory
>& xShapeFactory
2659 , const Reference
< uno::XComponentContext
>& xContext
2662 std::vector
< ViewLegendEntry
> aResult
;
2664 if( ! ( xShapeFactory
.is() && xTarget
.is() && xContext
.is() ) )
2669 ViewLegendEntry aEntry
;
2670 OUString aLabelText
;
2671 bool bVaryColorsByPoint
= rSeries
.isVaryColorsByPoint();
2672 bool bIsPie
= m_xChartTypeModel
->getChartType().equalsIgnoreAsciiCase(
2673 CHART2_SERVICE_NAME_CHARTTYPE_PIE
);
2676 if (bIsPie
&& m_xChartTypeModelProps
.is())
2678 bool bDonut
= false;
2679 if ((m_xChartTypeModelProps
->getPropertyValue("UseRings") >>= bDonut
) && bDonut
)
2683 catch (const uno::Exception
&)
2687 if (bVaryColorsByPoint
|| bIsPie
)
2689 Sequence
< OUString
> aCategoryNames
;
2690 if( m_pExplicitCategoriesProvider
)
2691 aCategoryNames
= m_pExplicitCategoriesProvider
->getSimpleCategories();
2692 Sequence
<sal_Int32
> deletedLegendEntries
;
2695 rSeries
.getPropertiesOfSeries()->getPropertyValue("DeletedLegendEntries") >>= deletedLegendEntries
;
2697 catch (const uno::Exception
&)
2700 for( sal_Int32 nIdx
=0; nIdx
<aCategoryNames
.getLength(); ++nIdx
)
2702 bool deletedLegendEntry
= false;
2703 for (auto& deletedLegendEntryIdx
: deletedLegendEntries
)
2705 if (nIdx
== deletedLegendEntryIdx
)
2707 deletedLegendEntry
= true;
2711 if (deletedLegendEntry
)
2715 uno::Reference
< drawing::XShapes
> xSymbolGroup( ShapeFactory::getOrCreateShapeFactory(xShapeFactory
)->createGroup2D( xTarget
));
2717 // create the symbol
2718 Reference
< drawing::XShape
> xShape( createLegendSymbolForPoint( rEntryKeyAspectRatio
,
2719 rSeries
, nIdx
, xSymbolGroup
, xShapeFactory
) );
2721 // set CID to symbol for selection
2724 aEntry
.aSymbol
.set( xSymbolGroup
, uno::UNO_QUERY
);
2726 OUString
aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_DATA_POINT
, nIdx
) );
2727 aChildParticle
= ObjectIdentifier::addChildParticle( aChildParticle
, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY
, 0 ) );
2728 OUString aCID
= ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries
.getSeriesParticle(), aChildParticle
);
2729 ShapeFactory::setShapeName( xShape
, aCID
);
2733 aLabelText
= aCategoryNames
[nIdx
];
2734 if( xShape
.is() || !aLabelText
.isEmpty() )
2736 aEntry
.aLabel
= FormattedStringHelper::createFormattedStringSequence( xContext
, aLabelText
, xTextProperties
);
2737 aResult
.push_back(aEntry
);
2744 uno::Reference
< drawing::XShapes
> xSymbolGroup( ShapeFactory::getOrCreateShapeFactory(xShapeFactory
)->createGroup2D( xTarget
));
2746 // create the symbol
2747 Reference
< drawing::XShape
> xShape( createLegendSymbolForSeries(
2748 rEntryKeyAspectRatio
, rSeries
, xSymbolGroup
, xShapeFactory
) );
2750 // set CID to symbol for selection
2753 aEntry
.aSymbol
.set( xSymbolGroup
, uno::UNO_QUERY
);
2755 OUString
aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY
, 0 ) );
2756 OUString aCID
= ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries
.getSeriesParticle(), aChildParticle
);
2757 ShapeFactory::setShapeName( xShape
, aCID
);
2761 aLabelText
= DataSeriesHelper::getDataSeriesLabel( rSeries
.getModel(), m_xChartTypeModel
.is() ? m_xChartTypeModel
->getRoleOfSequenceForSeriesLabel() : "values-y");
2762 aEntry
.aLabel
= FormattedStringHelper::createFormattedStringSequence( xContext
, aLabelText
, xTextProperties
);
2764 aResult
.push_back(aEntry
);
2767 // don't show legend entry of regression curve & friends if this type of chart
2768 // doesn't support statistics #i63016#, fdo#37197
2769 if (!ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel
, m_nDimension
))
2772 Reference
< XRegressionCurveContainer
> xRegrCont( rSeries
.getModel(), uno::UNO_QUERY
);
2775 Sequence
< Reference
< XRegressionCurve
> > aCurves( xRegrCont
->getRegressionCurves());
2776 sal_Int32 i
= 0, nCount
= aCurves
.getLength();
2777 for( i
=0; i
<nCount
; ++i
)
2779 if( aCurves
[i
].is() )
2782 OUString
aResStr( RegressionCurveHelper::getUINameForRegressionCurve( aCurves
[i
] ) );
2783 replaceParamterInString( aResStr
, "%SERIESNAME", aLabelText
);
2784 aEntry
.aLabel
= FormattedStringHelper::createFormattedStringSequence( xContext
, aResStr
, xTextProperties
);
2787 uno::Reference
< drawing::XShapes
> xSymbolGroup( ShapeFactory::getOrCreateShapeFactory(xShapeFactory
)->createGroup2D( xTarget
));
2789 // create the symbol
2790 Reference
< drawing::XShape
> xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio
,
2791 xSymbolGroup
, LegendSymbolStyle::Line
, xShapeFactory
,
2792 Reference
< beans::XPropertySet
>( aCurves
[i
], uno::UNO_QUERY
),
2793 VLegendSymbolFactory::PropertyType::Line
, uno::Any() ));
2795 // set CID to symbol for selection
2798 aEntry
.aSymbol
.set( xSymbolGroup
, uno::UNO_QUERY
);
2800 bool bAverageLine
= RegressionCurveHelper::isMeanValueLine( aCurves
[i
] );
2801 ObjectType eObjectType
= bAverageLine
? OBJECTTYPE_DATA_AVERAGE_LINE
: OBJECTTYPE_DATA_CURVE
;
2802 OUString
aChildParticle( ObjectIdentifier::createChildParticleWithIndex( eObjectType
, i
) );
2803 aChildParticle
= ObjectIdentifier::addChildParticle( aChildParticle
, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY
, 0 ) );
2804 OUString aCID
= ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries
.getSeriesParticle(), aChildParticle
);
2805 ShapeFactory::setShapeName( xShape
, aCID
);
2808 aResult
.push_back(aEntry
);
2813 catch( const uno::Exception
& )
2815 DBG_UNHANDLED_EXCEPTION("chart2" );
2820 VSeriesPlotter
* VSeriesPlotter::createSeriesPlotter(
2821 const uno::Reference
<XChartType
>& xChartTypeModel
2822 , sal_Int32 nDimensionCount
2823 , bool bExcludingPositioning
)
2825 if (!xChartTypeModel
.is())
2828 OUString aChartType
= xChartTypeModel
->getChartType();
2830 VSeriesPlotter
* pRet
=nullptr;
2831 if( aChartType
.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_COLUMN
) )
2832 pRet
= new BarChart(xChartTypeModel
,nDimensionCount
);
2833 else if( aChartType
.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_BAR
) )
2834 pRet
= new BarChart(xChartTypeModel
,nDimensionCount
);
2835 else if( aChartType
.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_AREA
) )
2836 pRet
= new AreaChart(xChartTypeModel
,nDimensionCount
,true);
2837 else if( aChartType
.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_LINE
) )
2838 pRet
= new AreaChart(xChartTypeModel
,nDimensionCount
,true,true);
2839 else if( aChartType
.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER
) )
2840 pRet
= new AreaChart(xChartTypeModel
,nDimensionCount
,false,true);
2841 else if( aChartType
.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE
) )
2842 pRet
= new BubbleChart(xChartTypeModel
,nDimensionCount
);
2843 else if( aChartType
.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE
) )
2844 pRet
= new PieChart(xChartTypeModel
,nDimensionCount
, bExcludingPositioning
);
2845 else if( aChartType
.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_NET
) )
2846 pRet
= new NetChart(xChartTypeModel
,nDimensionCount
,true,std::make_unique
<PolarPlottingPositionHelper
>());
2847 else if( aChartType
.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET
) )
2848 pRet
= new NetChart(xChartTypeModel
,nDimensionCount
,false,std::make_unique
<PolarPlottingPositionHelper
>());
2849 else if( aChartType
.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK
) )
2850 pRet
= new CandleStickChart(xChartTypeModel
,nDimensionCount
);
2852 pRet
= new AreaChart(xChartTypeModel
,nDimensionCount
,false,true);
2858 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */