summaryrefslogtreecommitdiffstats
path: root/chart2/source/view/main/VDataSeries.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'chart2/source/view/main/VDataSeries.cxx')
-rw-r--r--chart2/source/view/main/VDataSeries.cxx1118
1 files changed, 1118 insertions, 0 deletions
diff --git a/chart2/source/view/main/VDataSeries.cxx b/chart2/source/view/main/VDataSeries.cxx
new file mode 100644
index 000000000..3fb12ebca
--- /dev/null
+++ b/chart2/source/view/main/VDataSeries.cxx
@@ -0,0 +1,1118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <limits>
+#include <memory>
+#include <VDataSeries.hxx>
+#include <DataSeries.hxx>
+#include <ObjectIdentifier.hxx>
+#include <CommonConverters.hxx>
+#include <LabelPositionHelper.hxx>
+#include <ChartType.hxx>
+#include <ChartTypeHelper.hxx>
+#include <RegressionCurveHelper.hxx>
+#include <unonames.hxx>
+
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/Symbol.hpp>
+#include <com/sun/star/chart2/XRegressionCurveCalculator.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+
+#include <osl/diagnose.h>
+#include <tools/color.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+
+namespace chart {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using ::com::sun::star::uno::Reference;
+
+void VDataSequence::init( const uno::Reference< data::XDataSequence >& xModel )
+{
+ m_xModel = xModel;
+ m_aValues = DataSequenceToDoubleSequence( xModel );
+}
+
+bool VDataSequence::is() const
+{
+ return m_xModel.is();
+}
+void VDataSequence::clear()
+{
+ m_xModel = nullptr;
+ m_aValues.realloc(0);
+}
+
+double VDataSequence::getValue( sal_Int32 index ) const
+{
+ if( 0<=index && index<m_aValues.getLength() )
+ return m_aValues[index];
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+sal_Int32 VDataSequence::detectNumberFormatKey( sal_Int32 index ) const
+{
+ sal_Int32 nNumberFormatKey = -1;
+
+ // -1 is allowed and means a key for the whole sequence
+ if( -1<=index && index<m_aValues.getLength() && m_xModel.is())
+ {
+ nNumberFormatKey = m_xModel->getNumberFormatKeyByIndex( index );
+ }
+
+ return nNumberFormatKey;
+}
+
+sal_Int32 VDataSequence::getLength() const
+{
+ return m_aValues.getLength();
+}
+
+namespace
+{
+struct lcl_LessXOfPoint
+{
+ bool operator() ( const std::vector< double >& first,
+ const std::vector< double >& second )
+ {
+ if( !first.empty() && !second.empty() )
+ {
+ return first[0]<second[0];
+ }
+ return false;
+ }
+};
+
+void lcl_clearIfNoValuesButTextIsContained( VDataSequence& rData, const uno::Reference<data::XDataSequence>& xDataSequence )
+{
+ //#i71686#, #i101968#, #i102428#
+ sal_Int32 nCount = rData.m_aValues.getLength();
+ for( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ if( !std::isnan( rData.m_aValues[i] ) )
+ return;
+ }
+ //no double value is contained
+ //is there any text?
+ uno::Sequence< OUString > aStrings( DataSequenceToStringSequence( xDataSequence ) );
+ sal_Int32 nTextCount = aStrings.getLength();
+ for( sal_Int32 j = 0; j < nTextCount; ++j )
+ {
+ if( !aStrings[j].isEmpty() )
+ {
+ rData.clear();
+ return;
+ }
+ }
+ //no content at all
+}
+
+void lcl_maybeReplaceNanWithZero( double& rfValue, sal_Int32 nMissingValueTreatment )
+{
+ if( nMissingValueTreatment == css::chart::MissingValueTreatment::USE_ZERO
+ && (std::isnan(rfValue) || std::isinf(rfValue)) )
+ rfValue = 0.0;
+}
+
+}
+
+VDataSeries::VDataSeries( const rtl::Reference< DataSeries >& xDataSeries )
+ : m_nPolygonIndex(0)
+ , m_fLogicMinX(0.0)
+ , m_fLogicMaxX(0.0)
+ , m_fLogicZPos(0.0)
+ , m_xDataSeries(xDataSeries)
+ , m_nPointCount(0)
+ , m_pValueSequenceForDataLabelNumberFormatDetection(&m_aValues_Y)
+ , m_fXMeanValue(std::numeric_limits<double>::quiet_NaN())
+ , m_fYMeanValue(std::numeric_limits<double>::quiet_NaN())
+ , m_eStackingDirection(StackingDirection_NO_STACKING)
+ , m_nAxisIndex(0)
+ , m_bConnectBars(false)
+ , m_bGroupBarsPerAxis(true)
+ , m_nStartingAngle(90)
+ , m_nGlobalSeriesIndex(0)
+ , m_nCurrentAttributedPoint(-1)
+ , m_nMissingValueTreatment(css::chart::MissingValueTreatment::LEAVE_GAP)
+ , m_bAllowPercentValueInDataLabel(false)
+ , mpOldSeries(nullptr)
+ , mnPercent(0.0)
+{
+ m_xDataSeriesProps = m_xDataSeries;
+
+ const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aDataSequences =
+ m_xDataSeries->getDataSequences2();
+
+ for(sal_Int32 nN = aDataSequences.size();nN--;)
+ {
+ uno::Reference<data::XDataSequence> xDataSequence( aDataSequences[nN]->getValues());
+ uno::Reference<beans::XPropertySet> xProp(xDataSequence, uno::UNO_QUERY );
+ if( xProp.is())
+ {
+ try
+ {
+ uno::Any aARole = xProp->getPropertyValue("Role");
+ OUString aRole;
+ aARole >>= aRole;
+
+ if (aRole == "values-x")
+ {
+ m_aValues_X.init( xDataSequence );
+ lcl_clearIfNoValuesButTextIsContained( m_aValues_X, xDataSequence );
+ }
+ else if (aRole =="values-y")
+ m_aValues_Y.init( xDataSequence );
+ else if (aRole == "values-min")
+ m_aValues_Y_Min.init( xDataSequence );
+ else if (aRole == "values-max")
+ m_aValues_Y_Max.init( xDataSequence );
+ else if (aRole == "values-first")
+ m_aValues_Y_First.init( xDataSequence );
+ else if (aRole == "values-last")
+ m_aValues_Y_Last.init( xDataSequence );
+ else if (aRole == "values-size")
+ m_aValues_Bubble_Size.init( xDataSequence );
+ else
+ {
+ VDataSequence aSequence;
+ aSequence.init(xDataSequence);
+ m_PropertyMap.insert(std::make_pair(aRole, aSequence));
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ }
+
+ //determine the point count
+ m_nPointCount = m_aValues_Y.getLength();
+ {
+ if( m_nPointCount < m_aValues_Bubble_Size.getLength() )
+ m_nPointCount = m_aValues_Bubble_Size.getLength();
+ if( m_nPointCount < m_aValues_Y_Min.getLength() )
+ m_nPointCount = m_aValues_Y_Min.getLength();
+ if( m_nPointCount < m_aValues_Y_Max.getLength() )
+ m_nPointCount = m_aValues_Y_Max.getLength();
+ if( m_nPointCount < m_aValues_Y_First.getLength() )
+ m_nPointCount = m_aValues_Y_First.getLength();
+ if( m_nPointCount < m_aValues_Y_Last.getLength() )
+ m_nPointCount = m_aValues_Y_Last.getLength();
+ }
+
+ if( !xDataSeries.is())
+ return;
+
+ try
+ {
+ //get AttributedDataPoints
+ xDataSeries->getPropertyValue("AttributedDataPoints") >>= m_aAttributedDataPointIndexList;
+
+ xDataSeries->getPropertyValue("StackingDirection") >>= m_eStackingDirection;
+
+ xDataSeries->getPropertyValue("AttachedAxisIndex") >>= m_nAxisIndex;
+ if(m_nAxisIndex<0)
+ m_nAxisIndex=0;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+VDataSeries::~VDataSeries()
+{
+}
+
+void VDataSeries::doSortByXValues()
+{
+ if( !(m_aValues_X.is() && m_aValues_X.m_aValues.hasElements()) )
+ return;
+
+ //prepare a vector for sorting
+ std::vector< std::vector< double > > aTmp;//outer vector are points, inner vector are the different values of the point
+ sal_Int32 nPointIndex = 0;
+ for( nPointIndex=0; nPointIndex < m_nPointCount; nPointIndex++ )
+ {
+ aTmp.push_back(
+ { ((nPointIndex < m_aValues_X.m_aValues.getLength()) ? m_aValues_X.m_aValues[nPointIndex]
+ : std::numeric_limits<double>::quiet_NaN()),
+ ((nPointIndex < m_aValues_Y.m_aValues.getLength()) ? m_aValues_Y.m_aValues[nPointIndex]
+ : std::numeric_limits<double>::quiet_NaN())
+ }
+ );
+ }
+
+ //do sort
+ std::stable_sort( aTmp.begin(), aTmp.end(), lcl_LessXOfPoint() );
+
+ //fill the sorted points back to the members
+ m_aValues_X.m_aValues.realloc( m_nPointCount );
+ auto pDoublesX = m_aValues_X.m_aValues.getArray();
+ m_aValues_Y.m_aValues.realloc( m_nPointCount );
+ auto pDoublesY = m_aValues_Y.m_aValues.getArray();
+
+ for( nPointIndex=0; nPointIndex < m_nPointCount; nPointIndex++ )
+ {
+ pDoublesX[nPointIndex]=aTmp[nPointIndex][0];
+ pDoublesY[nPointIndex]=aTmp[nPointIndex][1];
+ }
+}
+
+void VDataSeries::releaseShapes()
+{
+ m_xGroupShape.set(nullptr);
+ m_xLabelsGroupShape.set(nullptr);
+ m_xErrorXBarsGroupShape.set(nullptr);
+ m_xErrorYBarsGroupShape.set(nullptr);
+ m_xFrontSubGroupShape.set(nullptr);
+ m_xBackSubGroupShape.set(nullptr);
+
+ m_aPolyPolygonShape3D.clear();
+ m_nPolygonIndex = 0;
+}
+
+const rtl::Reference<::chart::DataSeries>& VDataSeries::getModel() const
+{
+ return m_xDataSeries;
+}
+
+void VDataSeries::setCategoryXAxis()
+{
+ m_aValues_X.clear();
+ m_bAllowPercentValueInDataLabel = true;
+}
+
+void VDataSeries::setXValues( const Reference< chart2::data::XDataSequence >& xValues )
+{
+ m_aValues_X.clear();
+ m_aValues_X.init( xValues );
+ m_bAllowPercentValueInDataLabel = true;
+}
+
+void VDataSeries::setXValuesIfNone( const Reference< chart2::data::XDataSequence >& xValues )
+{
+ if( m_aValues_X.is() )
+ return;
+
+ m_aValues_X.init( xValues );
+ lcl_clearIfNoValuesButTextIsContained( m_aValues_X, xValues );
+}
+
+void VDataSeries::setGlobalSeriesIndex( sal_Int32 nGlobalSeriesIndex )
+{
+ m_nGlobalSeriesIndex = nGlobalSeriesIndex;
+}
+
+void VDataSeries::setParticle( const OUString& rSeriesParticle )
+{
+ m_aSeriesParticle = rSeriesParticle;
+
+ //get CID
+ m_aCID = ObjectIdentifier::createClassifiedIdentifierForParticle( m_aSeriesParticle );
+ m_aPointCID_Stub = ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT, m_aSeriesParticle );
+
+ m_aLabelCID_Stub = ObjectIdentifier::createClassifiedIdentifierWithParent(
+ OBJECTTYPE_DATA_LABEL, u"", getLabelsCID() );
+}
+OUString VDataSeries::getErrorBarsCID(bool bYError) const
+{
+ OUString aChildParticle( ObjectIdentifier::getStringForType(
+ bYError ? OBJECTTYPE_DATA_ERRORS_Y : OBJECTTYPE_DATA_ERRORS_X )
+ + "=" );
+
+ return ObjectIdentifier::createClassifiedIdentifierForParticles(
+ m_aSeriesParticle, aChildParticle );
+}
+OUString VDataSeries::getLabelsCID() const
+{
+ OUString aChildParticle( ObjectIdentifier::getStringForType( OBJECTTYPE_DATA_LABELS ) + "=" );
+
+ return ObjectIdentifier::createClassifiedIdentifierForParticles(
+ m_aSeriesParticle, aChildParticle );
+}
+OUString VDataSeries::getDataCurveCID( sal_Int32 nCurveIndex, bool bAverageLine ) const
+{
+ return ObjectIdentifier::createDataCurveCID( m_aSeriesParticle, nCurveIndex, bAverageLine );
+}
+
+OUString VDataSeries::getDataCurveEquationCID( sal_Int32 nCurveIndex ) const
+{
+ return ObjectIdentifier::createDataCurveEquationCID( m_aSeriesParticle, nCurveIndex );
+}
+void VDataSeries::setPageReferenceSize( const awt::Size & rPageRefSize )
+{
+ m_aReferenceSize = rPageRefSize;
+}
+
+void VDataSeries::setConnectBars( bool bConnectBars )
+{
+ m_bConnectBars = bConnectBars;
+}
+
+bool VDataSeries::getConnectBars() const
+{
+ return m_bConnectBars;
+}
+
+void VDataSeries::setGroupBarsPerAxis( bool bGroupBarsPerAxis )
+{
+ m_bGroupBarsPerAxis = bGroupBarsPerAxis;
+}
+
+bool VDataSeries::getGroupBarsPerAxis() const
+{
+ return m_bGroupBarsPerAxis;
+}
+
+void VDataSeries::setStartingAngle( sal_Int32 nStartingAngle )
+{
+ m_nStartingAngle = nStartingAngle;
+}
+
+sal_Int32 VDataSeries::getStartingAngle() const
+{
+ return m_nStartingAngle;
+}
+
+chart2::StackingDirection VDataSeries::getStackingDirection() const
+{
+ return m_eStackingDirection;
+}
+
+sal_Int32 VDataSeries::getAttachedAxisIndex() const
+{
+ return m_nAxisIndex;
+}
+
+void VDataSeries::setAttachedAxisIndex( sal_Int32 nAttachedAxisIndex )
+{
+ if( nAttachedAxisIndex < 0 )
+ nAttachedAxisIndex = 0;
+ m_nAxisIndex = nAttachedAxisIndex;
+}
+
+double VDataSeries::getXValue( sal_Int32 index ) const
+{
+ double fRet = std::numeric_limits<double>::quiet_NaN();
+ if(m_aValues_X.is())
+ {
+ if( 0<=index && index<m_aValues_X.getLength() )
+ {
+ fRet = m_aValues_X.m_aValues[index];
+ if(mpOldSeries && index < mpOldSeries->m_aValues_X.getLength())
+ {
+ double nOldVal = mpOldSeries->m_aValues_X.m_aValues[index];
+ fRet = nOldVal + (fRet - nOldVal) * mnPercent;
+ }
+ }
+ }
+ else
+ {
+ // #i70133# always return correct X position - needed for short data series
+ if( 0<=index /*&& index < m_nPointCount*/ )
+ fRet = index+1;//first category (index 0) matches with real number 1.0
+ }
+ lcl_maybeReplaceNanWithZero( fRet, getMissingValueTreatment() );
+ return fRet;
+}
+
+double VDataSeries::getYValue( sal_Int32 index ) const
+{
+ double fRet = std::numeric_limits<double>::quiet_NaN();
+ if(m_aValues_Y.is())
+ {
+ if( 0<=index && index<m_aValues_Y.getLength() )
+ {
+ fRet = m_aValues_Y.m_aValues[index];
+ if(mpOldSeries && index < mpOldSeries->m_aValues_Y.getLength())
+ {
+ double nOldVal = mpOldSeries->m_aValues_Y.m_aValues[index];
+ fRet = nOldVal + (fRet - nOldVal) * mnPercent;
+ }
+ }
+ }
+ else
+ {
+ // #i70133# always return correct X position - needed for short data series
+ if( 0<=index /*&& index < m_nPointCount*/ )
+ fRet = index+1;//first category (index 0) matches with real number 1.0
+ }
+ lcl_maybeReplaceNanWithZero( fRet, getMissingValueTreatment() );
+ return fRet;
+}
+
+void VDataSeries::getMinMaxXValue(double& fMin, double& fMax) const
+{
+ fMax = std::numeric_limits<double>::quiet_NaN();
+ fMin = std::numeric_limits<double>::quiet_NaN();
+
+ uno::Sequence< double > aValuesX = getAllX();
+
+ if(!aValuesX.hasElements())
+ return;
+
+ sal_Int32 i = 0;
+ while ( i < aValuesX.getLength() && std::isnan(aValuesX[i]) )
+ i++;
+ if ( i < aValuesX.getLength() )
+ fMax = fMin = aValuesX[i++];
+
+ for ( ; i < aValuesX.getLength(); i++)
+ {
+ const double aValue = aValuesX[i];
+ if ( aValue > fMax)
+ {
+ fMax = aValue;
+ }
+ else if ( aValue < fMin)
+ {
+ fMin = aValue;
+ }
+ }
+}
+double VDataSeries::getY_Min( sal_Int32 index ) const
+{
+ return m_aValues_Y_Min.getValue( index );
+}
+double VDataSeries::getY_Max( sal_Int32 index ) const
+{
+ return m_aValues_Y_Max.getValue( index );
+}
+double VDataSeries::getY_First( sal_Int32 index ) const
+{
+ return m_aValues_Y_First.getValue( index );
+}
+double VDataSeries::getY_Last( sal_Int32 index ) const
+{
+ return m_aValues_Y_Last.getValue( index );
+}
+double VDataSeries::getBubble_Size( sal_Int32 index ) const
+{
+ double nNewVal = m_aValues_Bubble_Size.getValue( index );
+ if(mpOldSeries && index < mpOldSeries->m_aValues_Bubble_Size.getLength())
+ {
+ double nOldVal = mpOldSeries->m_aValues_Bubble_Size.getValue( index );
+ nNewVal = nOldVal + (nNewVal - nOldVal) * mnPercent;
+ }
+
+ return nNewVal;
+}
+
+bool VDataSeries::hasExplicitNumberFormat( sal_Int32 nPointIndex, bool bForPercentage ) const
+{
+ OUString aPropName = bForPercentage ? OUString("PercentageNumberFormat") : OUString(CHART_UNONAME_NUMFMT);
+ bool bHasNumberFormat = false;
+ bool bLinkToSource = true;
+ uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( nPointIndex ));
+ if( xPointProp.is() && (xPointProp->getPropertyValue(CHART_UNONAME_LINK_TO_SRC_NUMFMT) >>= bLinkToSource))
+ {
+ sal_Int32 nNumberFormat = -1;
+ if( !bLinkToSource && (xPointProp->getPropertyValue(aPropName) >>= nNumberFormat))
+ bHasNumberFormat = true;
+ }
+ return bHasNumberFormat;
+}
+sal_Int32 VDataSeries::getExplicitNumberFormat( sal_Int32 nPointIndex, bool bForPercentage ) const
+{
+ OUString aPropName = bForPercentage ? OUString("PercentageNumberFormat") : OUString(CHART_UNONAME_NUMFMT);
+ sal_Int32 nNumberFormat = -1;
+ uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( nPointIndex ));
+ if( xPointProp.is() )
+ xPointProp->getPropertyValue(aPropName) >>= nNumberFormat;
+ return nNumberFormat;
+}
+void VDataSeries::setRoleOfSequenceForDataLabelNumberFormatDetection( std::u16string_view rRole )
+{
+ if (rRole == u"values-y")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y;
+ else if (rRole == u"values-size")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Bubble_Size;
+ else if (rRole == u"values-min")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_Min;
+ else if (rRole == u"values-max")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_Max;
+ else if (rRole == u"values-first")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_First;
+ else if (rRole == u"values-last")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_Last;
+ else if (rRole == u"values-x")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_X;
+}
+sal_Int32 VDataSeries::detectNumberFormatKey( sal_Int32 index ) const
+{
+ sal_Int32 nRet = 0;
+ if( m_pValueSequenceForDataLabelNumberFormatDetection )
+ nRet = m_pValueSequenceForDataLabelNumberFormatDetection->detectNumberFormatKey( index );
+ return nRet;
+}
+
+sal_Int32 VDataSeries::getLabelPlacement( sal_Int32 nPointIndex, const rtl::Reference< ChartType >& xChartType, bool bSwapXAndY ) const
+{
+ sal_Int32 nLabelPlacement=0;
+ try
+ {
+ uno::Reference< beans::XPropertySet > xPointProps( getPropertiesOfPoint( nPointIndex ) );
+ if( xPointProps.is() )
+ xPointProps->getPropertyValue("LabelPlacement") >>= nLabelPlacement;
+
+ const uno::Sequence < sal_Int32 > aAvailablePlacements( ChartTypeHelper::getSupportedLabelPlacements(
+ xChartType, bSwapXAndY, m_xDataSeries ) );
+
+ for( sal_Int32 n : aAvailablePlacements )
+ if( n == nLabelPlacement )
+ return nLabelPlacement; //ok
+
+ //otherwise use the first supported one
+ if( aAvailablePlacements.hasElements() )
+ {
+ nLabelPlacement = aAvailablePlacements[0];
+ if( xPointProps.is() )
+ xPointProps->setPropertyValue("LabelPlacement", uno::Any(nLabelPlacement));
+ return nLabelPlacement;
+ }
+
+ OSL_FAIL("no label placement supported");
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return nLabelPlacement;
+}
+
+awt::Point VDataSeries::getLabelPosition( awt::Point aTextShapePos, sal_Int32 nPointIndex ) const
+{
+ awt::Point aPos(-1, -1);
+ try
+ {
+ RelativePosition aCustomLabelPosition;
+ uno::Reference< beans::XPropertySet > xPointProps(getPropertiesOfPoint(nPointIndex));
+ if( xPointProps.is() && (xPointProps->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition))
+ {
+ aPos.X = static_cast<sal_Int32>(aCustomLabelPosition.Primary * m_aReferenceSize.Width) + aTextShapePos.X;
+ aPos.Y = static_cast<sal_Int32>(aCustomLabelPosition.Secondary * m_aReferenceSize.Height) + aTextShapePos.Y;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "");
+ }
+ return aPos;
+}
+
+bool VDataSeries::isLabelCustomPos(sal_Int32 nPointIndex) const
+{
+ bool bCustom = false;
+ try
+ {
+ if( isAttributedDataPoint(nPointIndex) )
+ {
+ uno::Reference< beans::XPropertySet > xPointProps(m_xDataSeries->getDataPointByIndex(nPointIndex));
+ RelativePosition aCustomLabelPosition;
+ if( xPointProps.is() && (xPointProps->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition) )
+ bCustom = true;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "");
+ }
+ return bCustom;
+}
+
+double VDataSeries::getMinimumofAllDifferentYValues( sal_Int32 index ) const
+{
+ double fMin = std::numeric_limits<double>::infinity();
+
+ if( !m_aValues_Y.is() &&
+ (m_aValues_Y_Min.is() || m_aValues_Y_Max.is()
+ || m_aValues_Y_First.is() || m_aValues_Y_Last.is() ) )
+ {
+ double fY_Min = getY_Min( index );
+ double fY_Max = getY_Max( index );
+ double fY_First = getY_First( index );
+ double fY_Last = getY_Last( index );
+
+ if(fMin>fY_First)
+ fMin=fY_First;
+ if(fMin>fY_Last)
+ fMin=fY_Last;
+ if(fMin>fY_Min)
+ fMin=fY_Min;
+ if(fMin>fY_Max)
+ fMin=fY_Max;
+ }
+ else
+ {
+ double fY = getYValue( index );
+ if(fMin>fY)
+ fMin=fY;
+ }
+
+ if( std::isinf(fMin) )
+ return std::numeric_limits<double>::quiet_NaN();
+
+ return fMin;
+}
+
+double VDataSeries::getMaximumofAllDifferentYValues( sal_Int32 index ) const
+{
+ double fMax = -std::numeric_limits<double>::infinity();
+
+ if( !m_aValues_Y.is() &&
+ (m_aValues_Y_Min.is() || m_aValues_Y_Max.is()
+ || m_aValues_Y_First.is() || m_aValues_Y_Last.is() ) )
+ {
+ double fY_Min = getY_Min( index );
+ double fY_Max = getY_Max( index );
+ double fY_First = getY_First( index );
+ double fY_Last = getY_Last( index );
+
+ if(fMax<fY_First)
+ fMax=fY_First;
+ if(fMax<fY_Last)
+ fMax=fY_Last;
+ if(fMax<fY_Min)
+ fMax=fY_Min;
+ if(fMax<fY_Max)
+ fMax=fY_Max;
+ }
+ else
+ {
+ double fY = getYValue( index );
+ if(fMax<fY)
+ fMax=fY;
+ }
+
+ if( std::isinf(fMax) )
+ return std::numeric_limits<double>::quiet_NaN();
+
+ return fMax;
+}
+
+uno::Sequence< double > const & VDataSeries::getAllX() const
+{
+ if(!m_aValues_X.is() && !m_aValues_X.getLength() && m_nPointCount)
+ {
+ //init x values from category indexes
+ //first category (index 0) matches with real number 1.0
+ m_aValues_X.m_aValues.realloc( m_nPointCount );
+ auto pDoubles = m_aValues_X.m_aValues.getArray();
+ for(sal_Int32 nN=m_aValues_X.getLength();nN--;)
+ pDoubles[nN] = nN+1;
+ }
+ return m_aValues_X.m_aValues;
+}
+
+uno::Sequence< double > const & VDataSeries::getAllY() const
+{
+ if(!m_aValues_Y.is() && !m_aValues_Y.getLength() && m_nPointCount)
+ {
+ //init y values from indexes
+ //first y-value (index 0) matches with real number 1.0
+ m_aValues_Y.m_aValues.realloc( m_nPointCount );
+ auto pDoubles = m_aValues_Y.m_aValues.getArray();
+ for(sal_Int32 nN=m_aValues_Y.getLength();nN--;)
+ pDoubles[nN] = nN+1;
+ }
+ return m_aValues_Y.m_aValues;
+}
+
+double VDataSeries::getXMeanValue() const
+{
+ if( std::isnan( m_fXMeanValue ) )
+ {
+ uno::Reference< XRegressionCurveCalculator > xCalculator( RegressionCurveHelper::createRegressionCurveCalculatorByServiceName( u"com.sun.star.chart2.MeanValueRegressionCurve" ) );
+ uno::Sequence< double > aXValuesDummy;
+ xCalculator->recalculateRegression( aXValuesDummy, getAllX() );
+ m_fXMeanValue = xCalculator->getCurveValue( 1.0 );
+ }
+ return m_fXMeanValue;
+}
+
+double VDataSeries::getYMeanValue() const
+{
+ if( std::isnan( m_fYMeanValue ) )
+ {
+ uno::Reference< XRegressionCurveCalculator > xCalculator(
+ RegressionCurveHelper::createRegressionCurveCalculatorByServiceName(u"com.sun.star.chart2.MeanValueRegressionCurve"));
+ uno::Sequence< double > aXValuesDummy;
+ xCalculator->recalculateRegression( aXValuesDummy, getAllY() );
+ m_fYMeanValue = xCalculator->getCurveValue( 1.0 );
+ }
+ return m_fYMeanValue;
+}
+
+static std::unique_ptr<Symbol> getSymbolPropertiesFromPropertySet( const uno::Reference< beans::XPropertySet >& xProp )
+{
+ std::unique_ptr< Symbol > apSymbolProps( new Symbol() );
+ try
+ {
+ if( xProp->getPropertyValue("Symbol") >>= *apSymbolProps )
+ {
+ //use main color to fill symbols
+ xProp->getPropertyValue("Color") >>= apSymbolProps->FillColor;
+ // border of symbols always same as fill color
+ apSymbolProps->BorderColor = apSymbolProps->FillColor;
+ }
+ else
+ apSymbolProps.reset();
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return apSymbolProps;
+}
+
+Symbol* VDataSeries::getSymbolProperties( sal_Int32 index ) const
+{
+ Symbol* pRet=nullptr;
+ if( isAttributedDataPoint( index ) )
+ {
+ adaptPointCache( index );
+ if (!m_apSymbolProperties_AttributedPoint)
+ m_apSymbolProperties_AttributedPoint
+ = getSymbolPropertiesFromPropertySet(getPropertiesOfPoint(index));
+ pRet = m_apSymbolProperties_AttributedPoint.get();
+ //if a single data point does not have symbols but the dataseries itself has symbols
+ //we create an invisible symbol shape to enable selection of that point
+ if( !pRet || pRet->Style == SymbolStyle_NONE )
+ {
+ if (!m_apSymbolProperties_Series)
+ m_apSymbolProperties_Series
+ = getSymbolPropertiesFromPropertySet(getPropertiesOfSeries());
+ if( m_apSymbolProperties_Series && m_apSymbolProperties_Series->Style != SymbolStyle_NONE )
+ {
+ if (!m_apSymbolProperties_InvisibleSymbolForSelection)
+ {
+ m_apSymbolProperties_InvisibleSymbolForSelection.reset(new Symbol);
+ m_apSymbolProperties_InvisibleSymbolForSelection->Style = SymbolStyle_STANDARD;
+ m_apSymbolProperties_InvisibleSymbolForSelection->StandardSymbol = 0;//square
+ m_apSymbolProperties_InvisibleSymbolForSelection->Size = com::sun::star::awt::Size(0, 0);//tdf#126033
+ m_apSymbolProperties_InvisibleSymbolForSelection->BorderColor = 0xff000000;//invisible
+ m_apSymbolProperties_InvisibleSymbolForSelection->FillColor = 0xff000000;//invisible
+ }
+ pRet = m_apSymbolProperties_InvisibleSymbolForSelection.get();
+ }
+ }
+ }
+ else
+ {
+ if (!m_apSymbolProperties_Series)
+ m_apSymbolProperties_Series
+ = getSymbolPropertiesFromPropertySet(getPropertiesOfSeries());
+ pRet = m_apSymbolProperties_Series.get();
+ }
+
+ if( pRet && pRet->Style == SymbolStyle_AUTO )
+ {
+ pRet->Style = SymbolStyle_STANDARD;
+
+ sal_Int32 nIndex = m_nGlobalSeriesIndex;
+ if(m_aValues_X.is())
+ nIndex++;
+ pRet->StandardSymbol = nIndex;
+ }
+
+ return pRet;
+}
+
+uno::Reference< beans::XPropertySet > VDataSeries::getXErrorBarProperties( sal_Int32 index ) const
+{
+ uno::Reference< beans::XPropertySet > xErrorBarProp;
+
+ uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( index ));
+ if( xPointProp.is() )
+ xPointProp->getPropertyValue(CHART_UNONAME_ERRORBAR_X) >>= xErrorBarProp;
+ return xErrorBarProp;
+}
+
+uno::Reference< beans::XPropertySet > VDataSeries::getYErrorBarProperties( sal_Int32 index ) const
+{
+ uno::Reference< beans::XPropertySet > xErrorBarProp;
+
+ uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( index ));
+ if( xPointProp.is() )
+ xPointProp->getPropertyValue(CHART_UNONAME_ERRORBAR_Y) >>= xErrorBarProp;
+ return xErrorBarProp;
+}
+
+bool VDataSeries::hasPointOwnColor( sal_Int32 index ) const
+{
+ if( !isAttributedDataPoint(index) )
+ return false;
+
+ try
+ {
+ uno::Reference< beans::XPropertyState > xPointState( getPropertiesOfPoint(index), uno::UNO_QUERY_THROW );
+ return (xPointState->getPropertyState("Color") != beans::PropertyState_DEFAULT_VALUE );
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return false;
+}
+
+bool VDataSeries::isAttributedDataPoint( sal_Int32 index ) const
+{
+ //returns true if the data point assigned by the given index has set its own properties
+ if( index>=m_nPointCount || m_nPointCount==0)
+ return false;
+ for(sal_Int32 n : m_aAttributedDataPointIndexList)
+ {
+ if(index == n)
+ return true;
+ }
+ return false;
+}
+
+bool VDataSeries::isVaryColorsByPoint() const
+{
+ bool bVaryColorsByPoint = false;
+ Reference< beans::XPropertySet > xSeriesProp( getPropertiesOfSeries() );
+ if( xSeriesProp.is() )
+ xSeriesProp->getPropertyValue("VaryColorsByPoint") >>= bVaryColorsByPoint;
+ return bVaryColorsByPoint;
+}
+
+uno::Reference< beans::XPropertySet > VDataSeries::getPropertiesOfPoint( sal_Int32 index ) const
+{
+ if( isAttributedDataPoint( index ) )
+ return m_xDataSeries->getDataPointByIndex(index);
+ return getPropertiesOfSeries();
+}
+
+const uno::Reference<beans::XPropertySet> & VDataSeries::getPropertiesOfSeries() const
+{
+ return m_xDataSeriesProps;
+}
+
+static std::unique_ptr<DataPointLabel> getDataPointLabelFromPropertySet( const uno::Reference< beans::XPropertySet >& xProp )
+{
+ std::unique_ptr< DataPointLabel > apLabel( new DataPointLabel() );
+ try
+ {
+ if( !(xProp->getPropertyValue(CHART_UNONAME_LABEL) >>= *apLabel) )
+ apLabel.reset();
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return apLabel;
+}
+
+void VDataSeries::adaptPointCache( sal_Int32 nNewPointIndex ) const
+{
+ if( m_nCurrentAttributedPoint != nNewPointIndex )
+ {
+ m_apLabel_AttributedPoint.reset();
+ m_apLabelPropNames_AttributedPoint.reset();
+ m_apLabelPropValues_AttributedPoint.reset();
+ m_apSymbolProperties_AttributedPoint.reset();
+ m_nCurrentAttributedPoint = nNewPointIndex;
+ }
+}
+
+DataPointLabel* VDataSeries::getDataPointLabel( sal_Int32 index ) const
+{
+ DataPointLabel* pRet = nullptr;
+ if( isAttributedDataPoint( index ) )
+ {
+ adaptPointCache( index );
+ if (!m_apLabel_AttributedPoint)
+ m_apLabel_AttributedPoint
+ = getDataPointLabelFromPropertySet(getPropertiesOfPoint(index));
+ pRet = m_apLabel_AttributedPoint.get();
+ }
+ else
+ {
+ if (!m_apLabel_Series)
+ m_apLabel_Series
+ = getDataPointLabelFromPropertySet(getPropertiesOfPoint(index));
+ pRet = m_apLabel_Series.get();
+ }
+ if( !m_bAllowPercentValueInDataLabel )
+ {
+ if( pRet )
+ pRet->ShowNumberInPercent = false;
+ }
+ return pRet;
+}
+
+DataPointLabel* VDataSeries::getDataPointLabelIfLabel( sal_Int32 index ) const
+{
+ DataPointLabel* pLabel = getDataPointLabel( index );
+ if( !pLabel || (!pLabel->ShowNumber && !pLabel->ShowNumberInPercent
+ && !pLabel->ShowCategoryName && !pLabel->ShowCustomLabel && !pLabel->ShowSeriesName ) )
+ return nullptr;
+ return pLabel;
+}
+
+bool VDataSeries::getTextLabelMultiPropertyLists( sal_Int32 index
+ , tNameSequence*& pPropNames
+ , tAnySequence*& pPropValues ) const
+{
+ pPropNames = nullptr; pPropValues = nullptr;
+ uno::Reference< beans::XPropertySet > xTextProp;
+ bool bDoDynamicFontResize = false;
+ if( isAttributedDataPoint( index ) )
+ {
+ adaptPointCache( index );
+ if (!m_apLabelPropValues_AttributedPoint)
+ {
+ // Cache these properties for this point.
+ m_apLabelPropNames_AttributedPoint.reset(new tNameSequence);
+ m_apLabelPropValues_AttributedPoint.reset(new tAnySequence);
+ xTextProp.set( getPropertiesOfPoint( index ));
+ PropertyMapper::getTextLabelMultiPropertyLists(
+ xTextProp, *m_apLabelPropNames_AttributedPoint, *m_apLabelPropValues_AttributedPoint);
+ bDoDynamicFontResize = true;
+ }
+ pPropNames = m_apLabelPropNames_AttributedPoint.get();
+ pPropValues = m_apLabelPropValues_AttributedPoint.get();
+ }
+ else
+ {
+ if (!m_apLabelPropValues_Series)
+ {
+ // Cache these properties for the whole series.
+ m_apLabelPropNames_Series.reset(new tNameSequence);
+ m_apLabelPropValues_Series.reset(new tAnySequence);
+ xTextProp.set( getPropertiesOfPoint( index ));
+ PropertyMapper::getTextLabelMultiPropertyLists(
+ xTextProp, *m_apLabelPropNames_Series, *m_apLabelPropValues_Series);
+ bDoDynamicFontResize = true;
+ }
+ pPropNames = m_apLabelPropNames_Series.get();
+ pPropValues = m_apLabelPropValues_Series.get();
+ }
+
+ if( bDoDynamicFontResize &&
+ pPropNames && pPropValues &&
+ xTextProp.is())
+ {
+ LabelPositionHelper::doDynamicFontResize( *pPropValues, *pPropNames, xTextProp, m_aReferenceSize );
+ }
+
+ return (pPropNames && pPropValues);
+}
+
+void VDataSeries::setMissingValueTreatment( sal_Int32 nMissingValueTreatment )
+{
+ m_nMissingValueTreatment = nMissingValueTreatment;
+}
+
+sal_Int32 VDataSeries::getMissingValueTreatment() const
+{
+ return m_nMissingValueTreatment;
+}
+
+VDataSeries::VDataSeries()
+ : m_nPolygonIndex(0)
+ , m_fLogicMinX(0)
+ , m_fLogicMaxX(0)
+ , m_fLogicZPos(0)
+ , m_nPointCount(0)
+ , m_pValueSequenceForDataLabelNumberFormatDetection(nullptr)
+ , m_fXMeanValue(0)
+ , m_fYMeanValue(0)
+ , m_eStackingDirection(chart2::StackingDirection_NO_STACKING)
+ , m_nAxisIndex(0)
+ , m_bConnectBars(false)
+ , m_bGroupBarsPerAxis(false)
+ , m_nStartingAngle(0)
+ , m_nGlobalSeriesIndex(0)
+ , m_nCurrentAttributedPoint(0)
+ , m_nMissingValueTreatment(0)
+ , m_bAllowPercentValueInDataLabel(false)
+ , mpOldSeries(nullptr)
+ , mnPercent(0)
+{
+}
+
+void VDataSeries::setOldTimeBased( VDataSeries* pOldSeries, double nPercent )
+{
+ mnPercent = nPercent;
+ mpOldSeries = pOldSeries;
+ mpOldSeries->mpOldSeries = nullptr;
+}
+
+VDataSeries* VDataSeries::createCopyForTimeBased() const
+{
+ VDataSeries* pNew = new VDataSeries();
+ pNew->m_aValues_X = m_aValues_X;
+ pNew->m_aValues_Y = m_aValues_Y;
+ pNew->m_aValues_Z = m_aValues_Z;
+ pNew->m_aValues_Y_Min = m_aValues_Y_Min;
+ pNew->m_aValues_Y_Max = m_aValues_Y_Max;
+ pNew->m_aValues_Y_First = m_aValues_Y_First;
+ pNew->m_aValues_Y_Last = m_aValues_Y_Last;
+ pNew->m_aValues_Bubble_Size = m_aValues_Bubble_Size;
+ pNew->m_PropertyMap = m_PropertyMap;
+
+ pNew->m_nPointCount = m_nPointCount;
+
+ return pNew;
+}
+
+double VDataSeries::getValueByProperty( sal_Int32 nIndex, const OUString& rPropName ) const
+{
+ auto const itr = m_PropertyMap.find(rPropName);
+ if (itr == m_PropertyMap.end())
+ return std::numeric_limits<double>::quiet_NaN();
+
+ const VDataSequence* pData = &itr->second;
+ double fValue = pData->getValue(nIndex);
+ if(mpOldSeries && mpOldSeries->hasPropertyMapping(rPropName))
+ {
+ double fOldValue = mpOldSeries->getValueByProperty( nIndex, rPropName );
+ if(rPropName.endsWith("Color"))
+ {
+ //optimized interpolation for color values
+ Color aColor(ColorTransparency, static_cast<sal_uInt32>(fValue));
+ Color aOldColor(ColorTransparency, static_cast<sal_uInt32>(fOldValue));
+ sal_uInt8 r = aOldColor.GetRed() + (aColor.GetRed() - aOldColor.GetRed()) * mnPercent;
+ sal_uInt8 g = aOldColor.GetGreen() + (aColor.GetGreen() - aOldColor.GetGreen()) * mnPercent;
+ sal_uInt8 b = aOldColor.GetBlue() + (aColor.GetBlue() - aOldColor.GetBlue()) * mnPercent;
+ sal_uInt8 a = aOldColor.GetAlpha() + (aColor.GetAlpha() - aOldColor.GetAlpha()) * mnPercent;
+ Color aRet(ColorAlpha, a, r, g, b);
+ return sal_uInt32(aRet);
+ }
+ return fOldValue + (fValue - fOldValue) * mnPercent;
+ }
+ return fValue;
+}
+
+bool VDataSeries::hasPropertyMapping(const OUString& rPropName ) const
+{
+ return m_PropertyMap.find(rPropName) != m_PropertyMap.end();
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */