chart2/source/inc/BaseCoordinateSystem.hxx | 6 chart2/source/inc/ExplicitCategoriesProvider.hxx | 3 chart2/source/model/main/BaseCoordinateSystem.cxx | 8 + chart2/source/tools/AxisHelper.cxx | 3 chart2/source/tools/ExplicitCategoriesProvider.cxx | 118 +++++++++-------- chart2/source/view/axes/VCartesianCoordinateSystem.cxx | 2 chart2/source/view/axes/VCoordinateSystem.cxx | 10 - chart2/source/view/axes/VPolarCoordinateSystem.cxx | 2 chart2/source/view/inc/VCoordinateSystem.hxx | 5 chart2/source/view/main/ChartView.cxx | 2 chart2/source/view/main/SeriesPlotterContainer.cxx | 16 +- chart2/source/view/main/SeriesPlotterContainer.hxx | 2 12 files changed, 93 insertions(+), 84 deletions(-)
New commits: commit eeb8ca44ac7994c0552749e190a193a44c956769 Author: Noel Grandin <noel.gran...@collabora.co.uk> AuthorDate: Fri Apr 4 10:02:06 2025 +0200 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Fri Apr 4 17:49:31 2025 +0200 tdf#147874 slow sheet switching with large chart2 (2) ExplicitCategoriesProvider is rather expensive to create, and we do it twice. So store it on the BaseCoordinateSystem object and share it, which means we only need to construct it once. This reduces switching time from 2s to 1s. Note that I am not sure that this is the best location to store this, the entire architecture of chart2 is a little opaque. Change-Id: Ic9dc9573f7495bcb76de14caa7cc7d5ad61d9ed1 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183700 Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> Tested-by: Jenkins diff --git a/chart2/source/inc/BaseCoordinateSystem.hxx b/chart2/source/inc/BaseCoordinateSystem.hxx index 71f226abcd51..e9e6f095c541 100644 --- a/chart2/source/inc/BaseCoordinateSystem.hxx +++ b/chart2/source/inc/BaseCoordinateSystem.hxx @@ -33,6 +33,8 @@ namespace chart { class Axis; class ChartType; +class ExplicitCategoriesProvider; +class ChartModel; namespace impl { @@ -100,6 +102,8 @@ public: void setChartTypes( const std::vector< rtl::Reference< ::chart::ChartType > >& aChartTypes ); const std::vector< rtl::Reference<::chart::ChartType > > & getChartTypes2() const { return m_aChartTypes; } + ExplicitCategoriesProvider& getExplicitCategoriesProvider(ChartModel& rModel); + protected: // ____ XModifyListener ____ @@ -122,6 +126,8 @@ private: typedef std::vector< std::vector< rtl::Reference< ::chart::Axis > > > tAxisVecVecType; tAxisVecVecType m_aAllAxis; //outer sequence is the dimension; inner sequence is the axis index that indicates main or secondary axis std::vector< rtl::Reference<::chart::ChartType > > m_aChartTypes; + // cache this here so we can share it across different parts of the code, it is expensive to create + std::unique_ptr<ExplicitCategoriesProvider> mxExplicitCategoriesProvider; }; } // namespace chart diff --git a/chart2/source/model/main/BaseCoordinateSystem.cxx b/chart2/source/model/main/BaseCoordinateSystem.cxx index 31473161e54e..607c04af0073 100644 --- a/chart2/source/model/main/BaseCoordinateSystem.cxx +++ b/chart2/source/model/main/BaseCoordinateSystem.cxx @@ -24,6 +24,7 @@ #include <ModifyListenerHelper.hxx> #include <Axis.hxx> #include <ChartType.hxx> +#include <ExplicitCategoriesProvider.hxx> #include <com/sun/star/chart2/AxisType.hpp> #include <com/sun/star/container/NoSuchElementException.hpp> #include <com/sun/star/lang/IndexOutOfBoundsException.hpp> @@ -318,6 +319,13 @@ void BaseCoordinateSystem::setChartTypes( const std::vector< rtl::Reference< Cha fireModifyEvent(); } +ExplicitCategoriesProvider& BaseCoordinateSystem::getExplicitCategoriesProvider(ChartModel& rModel) +{ + if (!mxExplicitCategoriesProvider) + mxExplicitCategoriesProvider = std::make_unique<ExplicitCategoriesProvider>(this, rModel); + return *mxExplicitCategoriesProvider; +} + // ____ XModifyBroadcaster ____ void SAL_CALL BaseCoordinateSystem::addModifyListener( const Reference< util::XModifyListener >& aListener ) { diff --git a/chart2/source/tools/AxisHelper.cxx b/chart2/source/tools/AxisHelper.cxx index 48da6b5a0e26..d82652fe3012 100644 --- a/chart2/source/tools/AxisHelper.cxx +++ b/chart2/source/tools/AxisHelper.cxx @@ -111,8 +111,7 @@ chart2::ScaleData AxisHelper::getDateCheckedScale( const rtl::Reference< Axis >& } if( aScale.AxisType == AxisType::DATE ) { - ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSys, rModel ); - if( !aExplicitCategoriesProvider.isDateAxis() ) + if( !xCooSys->getExplicitCategoriesProvider(rModel).isDateAxis() ) aScale.AxisType = AxisType::CATEGORY; } return aScale; diff --git a/chart2/source/view/axes/VCartesianCoordinateSystem.cxx b/chart2/source/view/axes/VCartesianCoordinateSystem.cxx index 033aeaecd23a..5b714f1375d0 100644 --- a/chart2/source/view/axes/VCartesianCoordinateSystem.cxx +++ b/chart2/source/view/axes/VCartesianCoordinateSystem.cxx @@ -136,7 +136,7 @@ void VCartesianCoordinateSystem::createVAxisList( continue; rtl::Reference<Diagram> xDiagram(xChartDoc->getFirstChartDiagram()); - AxisProperties aAxisProperties(xAxis, getExplicitCategoriesProvider(), xDiagram->getDataTableRef()); + AxisProperties aAxisProperties(xAxis, &m_xCooSysModel->getExplicitCategoriesProvider(*xChartDoc), xDiagram->getDataTableRef()); aAxisProperties.m_nDimensionIndex = nDimensionIndex; aAxisProperties.m_bSwapXAndY = bSwapXAndY; aAxisProperties.m_bIsMainAxis = (nAxisIndex==0); diff --git a/chart2/source/view/axes/VCoordinateSystem.cxx b/chart2/source/view/axes/VCoordinateSystem.cxx index 90bc5ed6bb7e..80d1f7d81c8e 100644 --- a/chart2/source/view/axes/VCoordinateSystem.cxx +++ b/chart2/source/view/axes/VCoordinateSystem.cxx @@ -220,16 +220,6 @@ void VCoordinateSystem::impl_adjustDimensionAndIndex( sal_Int32& rDimensionIndex rAxisIndex = 0; } -void VCoordinateSystem::setExplicitCategoriesProvider( ExplicitCategoriesProvider* pExplicitCategoriesProvider /*takes ownership*/ ) -{ - m_apExplicitCategoriesProvider.reset(pExplicitCategoriesProvider); -} - -ExplicitCategoriesProvider* VCoordinateSystem::getExplicitCategoriesProvider() -{ - return m_apExplicitCategoriesProvider.get(); -} - std::vector< ExplicitScaleData > VCoordinateSystem::getExplicitScales( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) const { std::vector< ExplicitScaleData > aRet(m_aExplicitScales); diff --git a/chart2/source/view/axes/VPolarCoordinateSystem.cxx b/chart2/source/view/axes/VPolarCoordinateSystem.cxx index 36f0f3e35ba5..d45aeca7b762 100644 --- a/chart2/source/view/axes/VPolarCoordinateSystem.cxx +++ b/chart2/source/view/axes/VPolarCoordinateSystem.cxx @@ -96,7 +96,7 @@ void VPolarCoordinateSystem::createVAxisList( continue; rtl::Reference<Diagram> xDiagram(xChartDoc->getFirstChartDiagram()); - AxisProperties aAxisProperties(xAxis,getExplicitCategoriesProvider(), xDiagram->getDataTableRef()); + AxisProperties aAxisProperties(xAxis, &m_xCooSysModel->getExplicitCategoriesProvider(*xChartDoc), xDiagram->getDataTableRef()); aAxisProperties.init(); if(aAxisProperties.m_bDisplayLabels) aAxisProperties.m_nNumberFormatKey = getNumberFormatKeyForAxis(xAxis, xChartDoc); diff --git a/chart2/source/view/inc/VCoordinateSystem.hxx b/chart2/source/view/inc/VCoordinateSystem.hxx index 61dda842d02b..381f137d4c09 100644 --- a/chart2/source/view/inc/VCoordinateSystem.hxx +++ b/chart2/source/view/inc/VCoordinateSystem.hxx @@ -77,9 +77,6 @@ public: ExplicitScaleData getExplicitScale( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) const; ExplicitIncrementData getExplicitIncrement( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) const; - void setExplicitCategoriesProvider( ExplicitCategoriesProvider* /*takes ownership*/ ); - ExplicitCategoriesProvider* getExplicitCategoriesProvider(); - // returns a complete scale set for a given dimension and index; for example if nDimensionIndex==1 and nAxisIndex==2 you get returned the secondary x axis, main y axis and main z axis std::vector< ExplicitScaleData > getExplicitScales( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) const; // returns a complete increment set for a given dimension and index; for example if nDimensionIndex==1 and nAxisIndex==2 you get returned the secondary x axis, main y axis and main z axis @@ -200,8 +197,6 @@ private: tFullExplicitScaleMap m_aSecondaryExplicitScales; tFullExplicitIncrementMap m_aSecondaryExplicitIncrements; - - std::unique_ptr< ExplicitCategoriesProvider > m_apExplicitCategoriesProvider; }; } //namespace chart diff --git a/chart2/source/view/main/ChartView.cxx b/chart2/source/view/main/ChartView.cxx index 7fc3ec03a6f7..b82df788bcf6 100644 --- a/chart2/source/view/main/ChartView.cxx +++ b/chart2/source/view/main/ChartView.cxx @@ -535,7 +535,7 @@ awt::Rectangle ChartView::impl_createDiagramAndContent( const CreateShapeParam2D // - prepare list of all axis and how they are used Date aNullDate = NumberFormatterWrapper( xNumberFormatsSupplier ).getNullDate(); - rParam.mpSeriesPlotterContainer->initAxisUsageList(aNullDate); + rParam.mpSeriesPlotterContainer->initAxisUsageList(aNullDate, mrChartModel); rParam.mpSeriesPlotterContainer->doAutoScaling( mrChartModel ); rParam.mpSeriesPlotterContainer->setScalesFromCooSysToPlotter(); rParam.mpSeriesPlotterContainer->setNumberFormatsFromAxes(); diff --git a/chart2/source/view/main/SeriesPlotterContainer.cxx b/chart2/source/view/main/SeriesPlotterContainer.cxx index 3f054b6ea5e0..714ae7288feb 100644 --- a/chart2/source/view/main/SeriesPlotterContainer.cxx +++ b/chart2/source/view/main/SeriesPlotterContainer.cxx @@ -125,7 +125,6 @@ VCoordinateSystem* SeriesPlotterContainer::addCooSysToList( ObjectIdentifier::createParticleForCoordinateSystem(xCooSys, &rChartModel)); pVCooSys->setParticle(aCooSysParticle); - pVCooSys->setExplicitCategoriesProvider(new ExplicitCategoriesProvider(xCooSys, rChartModel)); rVCooSysList.push_back(std::move(pVCooSys)); return rVCooSysList.back().get(); } @@ -258,7 +257,8 @@ void SeriesPlotterContainer::initializeCooSysAndSeriesPlotter(ChartModel& rChart pPlotter->setNumberFormatsSupplier(rChartModel.getNumberFormatsSupplier()); pPlotter->setColorScheme(xColorScheme); if (pVCooSys) - pPlotter->setExplicitCategoriesProvider(pVCooSys->getExplicitCategoriesProvider()); + pPlotter->setExplicitCategoriesProvider( + &xCooSys->getExplicitCategoriesProvider(rChartModel)); sal_Int32 nMissingValueTreatment = xDiagram->getCorrectedMissingValueTreatment(xChartType); @@ -375,7 +375,7 @@ bool SeriesPlotterContainer::isCategoryPositionShifted(const chart2::ScaleData& return rSourceScale.AxisType == AxisType::SERIES; } -void SeriesPlotterContainer::initAxisUsageList(const Date& rNullDate) +void SeriesPlotterContainer::initAxisUsageList(const Date& rNullDate, ChartModel& rChartModel) { m_aAxisUsageList.clear(); @@ -407,13 +407,13 @@ void SeriesPlotterContainer::initAxisUsageList(const Date& rNullDate) // Create axis usage object for this axis. chart2::ScaleData aSourceScale = xAxis->getScaleData(); - ExplicitCategoriesProvider* pCatProvider - = pVCooSys->getExplicitCategoriesProvider(); + ExplicitCategoriesProvider& rCatProvider + = xCooSys->getExplicitCategoriesProvider(rChartModel); if (nDimIndex == 0) - AxisHelper::checkDateAxis(aSourceScale, pCatProvider, bDateAxisAllowed); + AxisHelper::checkDateAxis(aSourceScale, &rCatProvider, bDateAxisAllowed); - bool bHasComplexCat = pCatProvider && pCatProvider->hasComplexCategories() - && bComplexCategoryAllowed; + bool bHasComplexCat + = rCatProvider.hasComplexCategories() && bComplexCategoryAllowed; aSourceScale.ShiftedCategoryPosition = isCategoryPositionShifted(aSourceScale, bHasComplexCat); diff --git a/chart2/source/view/main/SeriesPlotterContainer.hxx b/chart2/source/view/main/SeriesPlotterContainer.hxx index 38f3c8b909c8..485d4210d646 100644 --- a/chart2/source/view/main/SeriesPlotterContainer.hxx +++ b/chart2/source/view/main/SeriesPlotterContainer.hxx @@ -63,7 +63,7 @@ public: * object and initialize its `aAutoScaling` member to the `ScaleData` * object of the current axis. */ - void initAxisUsageList(const Date& rNullDate); + void initAxisUsageList(const Date& rNullDate, ChartModel& rChartModel); /** * Perform automatic axis scaling and determine the amount and spacing of commit 8611749d0acbf910a7003131e4419022eb4fe4bc Author: Noel Grandin <noel.gran...@collabora.co.uk> AuthorDate: Thu Apr 3 17:21:03 2025 +0200 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Fri Apr 4 17:49:22 2025 +0200 tdf#147874 slow sheet switching with large chart2 split out the initialisation and exit early if possible. Takes the switching time from 11sec to 2sec for me. Change-Id: If31962e9603a14271a638da19a26ea1b050967d2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183699 Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> Tested-by: Jenkins diff --git a/chart2/source/inc/ExplicitCategoriesProvider.hxx b/chart2/source/inc/ExplicitCategoriesProvider.hxx index a7b97214f628..9e43abfc268e 100644 --- a/chart2/source/inc/ExplicitCategoriesProvider.hxx +++ b/chart2/source/inc/ExplicitCategoriesProvider.hxx @@ -27,6 +27,7 @@ #include <vector> namespace chart { class ChartModel; } +namespace com::sun::star::chart2::data { class XDataProvider; } namespace com::sun::star::chart2::data { class XDataSequence; } namespace com::sun::star::chart2::data { class XLabeledDataSequence; } namespace com::sun::star::uno { class Any; } @@ -93,6 +94,8 @@ private: ExplicitCategoriesProvider(ExplicitCategoriesProvider const &) = delete; ExplicitCategoriesProvider& operator =(ExplicitCategoriesProvider const &) = delete; + void implInitSplit(); + bool volatile m_bDirty; unotools::WeakReference< ::chart::BaseCoordinateSystem > m_xCooSysModel; ChartModel& mrModel; diff --git a/chart2/source/tools/ExplicitCategoriesProvider.cxx b/chart2/source/tools/ExplicitCategoriesProvider.cxx index d543b2b365fb..9a3ced3ba49d 100644 --- a/chart2/source/tools/ExplicitCategoriesProvider.cxx +++ b/chart2/source/tools/ExplicitCategoriesProvider.cxx @@ -75,70 +75,78 @@ ExplicitCategoriesProvider::ExplicitCategoriesProvider( const rtl::Reference< Ba if( m_xOriginalCategories.is() ) { - uno::Reference< data::XDataProvider > xDataProvider( mrModel.getDataProvider() ); + implInitSplit(); + if( m_aSplitCategoriesList.empty() ) + m_aSplitCategoriesList = { m_xOriginalCategories }; + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } +} - OUString aCategoriesRange( DataSourceHelper::getRangeFromValues( m_xOriginalCategories ) ); - if( xDataProvider.is() && !aCategoriesRange.isEmpty() ) - { - const bool bFirstCellAsLabel = false; - const bool bHasCategories = false; - const uno::Sequence< sal_Int32 > aSequenceMapping; +void ExplicitCategoriesProvider::implInitSplit() +{ + uno::Reference< data::XDataProvider > xDataProvider( mrModel.getDataProvider() ); + if( !xDataProvider.is() ) + return; - uno::Reference< data::XDataSource > xColumnCategoriesSource( xDataProvider->createDataSource( - DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, true /*bUseColumns*/ - , bFirstCellAsLabel, bHasCategories ) ) ); + OUString aCategoriesRange( DataSourceHelper::getRangeFromValues( m_xOriginalCategories ) ); + if( aCategoriesRange.isEmpty() ) + return; - uno::Reference< data::XDataSource > xRowCategoriesSource( xDataProvider->createDataSource( - DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, false /*bUseColumns*/ - , bFirstCellAsLabel, bHasCategories ) ) ); + const bool bFirstCellAsLabel = false; + const bool bHasCategories = false; + const uno::Sequence< sal_Int32 > aSequenceMapping; - if( xColumnCategoriesSource.is() && xRowCategoriesSource.is() ) - { - Sequence< Reference< data::XLabeledDataSequence> > aColumns = xColumnCategoriesSource->getDataSequences(); - Sequence< Reference< data::XLabeledDataSequence> > aRows = xRowCategoriesSource->getDataSequences(); + uno::Reference< data::XDataSource > xRowCategoriesSource( xDataProvider->createDataSource( + DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, false /*bUseColumns*/ + , bFirstCellAsLabel, bHasCategories ) ) ); + if( !xRowCategoriesSource ) + return; - sal_Int32 nColumnCount = aColumns.getLength(); - sal_Int32 nRowCount = aRows.getLength(); - if( nColumnCount>1 && nRowCount>1 ) - { - //we have complex categories - //->split them in the direction of the first series - //detect whether the first series is a row or a column - bool bSeriesUsesColumns = true; - std::vector< rtl::Reference< DataSeries > > aSeries = ChartModelHelper::getDataSeries( &mrModel ); - if( !aSeries.empty() ) - { - const rtl::Reference< DataSeries >& xSeriesSource = aSeries.front(); - for(const auto& rArgument : xDataProvider->detectArguments( xSeriesSource)) - { - if ( rArgument.Name == "DataRowSource" ) - { - css::chart::ChartDataRowSource eRowSource; - if( rArgument.Value >>= eRowSource ) - { - bSeriesUsesColumns = (eRowSource == css::chart::ChartDataRowSource_COLUMNS); - break; - } - } - } - } - if( bSeriesUsesColumns ) - m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aColumns); - else - m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aRows); - } - } - } - if( m_aSplitCategoriesList.empty() ) + Sequence< Reference< data::XLabeledDataSequence> > aRows = xRowCategoriesSource->getDataSequences(); + sal_Int32 nRowCount = aRows.getLength(); + if( nRowCount<=1 ) + return; + + uno::Reference< data::XDataSource > xColumnCategoriesSource( xDataProvider->createDataSource( + DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, true /*bUseColumns*/ + , bFirstCellAsLabel, bHasCategories ) ) ); + if( !xColumnCategoriesSource ) + return; + + Sequence< Reference< data::XLabeledDataSequence> > aColumns = xColumnCategoriesSource->getDataSequences(); + sal_Int32 nColumnCount = aColumns.getLength(); + if( nColumnCount<=1 ) + return; + + //we have complex categories + //->split them in the direction of the first series + //detect whether the first series is a row or a column + bool bSeriesUsesColumns = true; + std::vector< rtl::Reference< DataSeries > > aSeries = ChartModelHelper::getDataSeries( &mrModel ); + if( !aSeries.empty() ) + { + const rtl::Reference< DataSeries >& xSeriesSource = aSeries.front(); + for(const auto& rArgument : xDataProvider->detectArguments( xSeriesSource)) + { + if ( rArgument.Name == "DataRowSource" ) { - m_aSplitCategoriesList = { m_xOriginalCategories }; + css::chart::ChartDataRowSource eRowSource; + if( rArgument.Value >>= eRowSource ) + { + bSeriesUsesColumns = (eRowSource == css::chart::ChartDataRowSource_COLUMNS); + break; + } } } } - catch( const uno::Exception & ) - { - DBG_UNHANDLED_EXCEPTION("chart2"); - } + if( bSeriesUsesColumns ) + m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aColumns); + else + m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aRows); } ExplicitCategoriesProvider::~ExplicitCategoriesProvider()