diff options
author | Kurt Nordback <kurt.nordback@protonmail.com> | 2023-10-31 20:29:34 -0600 |
---|---|---|
committer | Noel Grandin <noel.grandin@collabora.co.uk> | 2024-02-16 08:07:12 +0100 |
commit | 63562694beb42b86d0f00dd3c18ac47bb094c9fe (patch) | |
tree | 8c56074750714c4ebb4224dbbb8f2ff766ab2bb3 | |
parent | 694b9b6422ee27da13ad455174cde50b1a673ac7 (diff) |
tdf#50934: Implement PieDataSrc and related structure
Change-Id: I6affb326600d77ddf756d9bc223e7dcc29f87d23
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160725
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
-rw-r--r-- | chart2/source/view/charttypes/PieChart.cxx | 148 | ||||
-rw-r--r-- | chart2/source/view/charttypes/PieChart.hxx | 80 |
2 files changed, 205 insertions, 23 deletions
diff --git a/chart2/source/view/charttypes/PieChart.cxx b/chart2/source/view/charttypes/PieChart.cxx index 201e226119ab..0424130c8d5e 100644 --- a/chart2/source/view/charttypes/PieChart.cxx +++ b/chart2/source/view/charttypes/PieChart.cxx @@ -855,20 +855,38 @@ void PieChart::createShapes() aParam.mfLogicYSum += fabs(fY); } - if (aParam.mfLogicYSum == 0.0) + if (aParam.mfLogicYSum == 0.0) { // Total sum of all Y values in this series is zero. Skip the whole series. continue; + } + + PieDataSrcBase *pDataSrc = nullptr; + PieDataSrc normalPieSrc; + OfPieDataSrc ofPieSrc; + + // Default to regular pie if too few points for of-pie + ::css::chart2::PieChartSubType eSubType = + nPointCount >= OfPieDataSrc::minPoints() ? + m_eSubType : + PieChartSubType_NONE; - switch (m_eSubType) { + switch (eSubType) { case PieChartSubType_NONE: - createOneRing(SubPieType::NONE, fSlotX, aParam, xSeriesTarget, xTextTarget, pSeries, n3DRelativeHeight); + pDataSrc = &normalPieSrc; + createOneRing(SubPieType::NONE, fSlotX, aParam, xSeriesTarget, + xTextTarget, pSeries, pDataSrc, n3DRelativeHeight); break; case PieChartSubType_BAR: - createOneRing(SubPieType::LEFT, 0, aParam, xSeriesTarget, xTextTarget, pSeries, n3DRelativeHeight); + pDataSrc = &ofPieSrc; + createOneRing(SubPieType::LEFT, 0, aParam, xSeriesTarget, + xTextTarget, pSeries, pDataSrc, n3DRelativeHeight); break; case PieChartSubType_PIE: - createOneRing(SubPieType::LEFT, 0, aParam, xSeriesTarget, xTextTarget, pSeries, n3DRelativeHeight); - createOneRing(SubPieType::RIGHT, 0, aParam, xSeriesTarget, xTextTarget, pSeries, n3DRelativeHeight); + pDataSrc = &ofPieSrc; + createOneRing(SubPieType::LEFT, 0, aParam, xSeriesTarget, + xTextTarget, pSeries, pDataSrc, n3DRelativeHeight); + createOneRing(SubPieType::RIGHT, 0, aParam, xSeriesTarget, + xTextTarget, pSeries, pDataSrc, n3DRelativeHeight); break; default: assert(false); // this shouldn't happen @@ -882,6 +900,7 @@ void PieChart::createOneRing([[maybe_unused]]enum SubPieType eType, const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, const rtl::Reference<SvxShapeGroup>& xTextTarget, VDataSeries* pSeries, + const PieDataSrcBase *pDataSrc, sal_Int32 n3DRelativeHeight) { bool bHasFillColorMapping = pSeries->hasPropertyMapping("FillColor"); @@ -891,10 +910,6 @@ void PieChart::createOneRing([[maybe_unused]]enum SubPieType eType, /// Counter-clockwise offset from the 3 o'clock position. m_aPosHelper.m_fAngleDegreeOffset = pSeries->getStartingAngle(); - ///iterate through all points to get the sum of all entries of - ///the current data series - sal_Int32 nPointCount=pSeries->getTotalPointCount(); - ///the `explodeable` ring is the first one except when the radius axis ///orientation is reversed (always!?) and we are dealing with a donut: in ///such a case the `explodeable` ring is the last one. @@ -902,9 +917,18 @@ void PieChart::createOneRing([[maybe_unused]]enum SubPieType eType, if( m_aPosHelper.isMathematicalOrientationRadius() && m_bUseRings ) nExplodeableSlot = m_aZSlots.front().size()-1; + sal_Int32 nRingPtCnt = pDataSrc->getNPoints(pSeries, eType); + + // Find sum of entries for this ring or sub-pie + double ringSum = 0; + for (sal_Int32 nPointIndex = 0; nPointIndex < nRingPtCnt; nPointIndex++ ) { + double fY = pDataSrc->getData(pSeries, nPointIndex, eType); + if (!std::isnan(fY) ) ringSum += fY; + } + double fLogicYForNextPoint = 0.0; ///iterate through all points to create shapes - for(sal_Int32 nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ ) + for(sal_Int32 nPointIndex = 0; nPointIndex < nRingPtCnt; nPointIndex++ ) { double fLogicInnerRadius, fLogicOuterRadius; @@ -923,8 +947,9 @@ void PieChart::createOneRing([[maybe_unused]]enum SubPieType eType, aParam.mfDepth = getTransformedDepth() * (n3DRelativeHeight / 100.0); rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries, xSeriesTarget); + ///collect data point information (logic coordinates, style ): - double fLogicYValue = fabs(pSeries->getYValue( nPointIndex )); + double fLogicYValue = pDataSrc->getData(pSeries, nPointIndex, eType); if( std::isnan(fLogicYValue) ) continue; if(fLogicYValue==0.0)//@todo: continue also if the resolution is too small @@ -932,13 +957,14 @@ void PieChart::createOneRing([[maybe_unused]]enum SubPieType eType, double fLogicYPos = fLogicYForNextPoint; fLogicYForNextPoint += fLogicYValue; - uno::Reference< beans::XPropertySet > xPointProperties = pSeries->getPropertiesOfPoint( nPointIndex ); + uno::Reference< beans::XPropertySet > xPointProperties = + pDataSrc->getProps(pSeries, nPointIndex, eType); //iterate through all subsystems to create partial points { //logic values on angle axis: - double fLogicStartAngleValue = fLogicYPos / aParam.mfLogicYSum; - double fLogicEndAngleValue = (fLogicYPos+fLogicYValue) / aParam.mfLogicYSum; + double fLogicStartAngleValue = fLogicYPos / ringSum; + double fLogicEndAngleValue = (fLogicYPos+fLogicYValue) / ringSum; ///note that the explode percentage is set to the `Offset` ///property of the current data series entry only for slices @@ -967,8 +993,9 @@ void PieChart::createOneRing([[maybe_unused]]enum SubPieType eType, // Do concentric explosion if it's a donut chart with more than one series const bool bConcentricExplosion = m_bUseRings && (m_aZSlots.front().size() > 1); rtl::Reference<SvxShape> xPointShape = - createDataPoint(eType, xSeriesGroupShape_Shapes, xPointProperties, aParam, nPointCount, - bConcentricExplosion); + createDataPoint(eType, xSeriesGroupShape_Shapes, + xPointProperties, aParam, nRingPtCnt, + bConcentricExplosion); ///point color: if (!pSeries->hasPointOwnColor(nPointIndex) && m_xColorScheme.is()) @@ -1828,6 +1855,93 @@ bool PieChart::performLabelBestFitInnerPlacement(ShapeParam& rShapeParam, PieLab return true; } +//======================= +// class PieDataSrc +//======================= +double PieDataSrc::getData(const VDataSeries* pSeries, sal_Int32 nPtIdx, + enum SubPieType /*eType*/) const +{ + return fabs(pSeries->getYValue( nPtIdx )); +} + +sal_Int32 PieDataSrc::getNPoints(const VDataSeries* pSeries, + [[maybe_unused]] enum SubPieType eType) const +{ + assert(eType == SubPieType::NONE); + return pSeries->getTotalPointCount(); +} + +uno::Reference< beans::XPropertySet > PieDataSrc::getProps( + const VDataSeries* pSeries, sal_Int32 nPtIdx, + [[maybe_unused]] enum SubPieType eType) const +{ + assert(eType == SubPieType::NONE); + return pSeries->getPropertiesOfPoint(nPtIdx); +} + + +//======================= +// class OfPieDataSrc +//======================= + +// For now, just implement the default Excel behavior, which is that the +// right pie consists of the last three entries in the series. Other +// behaviors should be supported later. +// TODO + +sal_Int32 OfPieDataSrc::getNPoints(const VDataSeries* pSeries, + enum SubPieType eType) const +{ + if (eType == SubPieType::LEFT) { + return pSeries->getTotalPointCount() - 2; + } else { + assert(eType == SubPieType::RIGHT); + return 3; + } +} + +double OfPieDataSrc::getData(const VDataSeries* pSeries, sal_Int32 nPtIdx, + enum SubPieType eType) const +{ + const sal_Int32 n = pSeries->getTotalPointCount() - 3; + if (eType == SubPieType::LEFT) { + // nPtIdx should be in [0, n] + if (nPtIdx < n) { + return fabs(pSeries->getYValue( nPtIdx )); + } else { + assert(nPtIdx == n); + return fabs(pSeries->getYValue(n)) + + fabs(pSeries->getYValue(n+1)) + + fabs(pSeries->getYValue(n+2)); + } + } else { + assert(eType == SubPieType::RIGHT); + return fabs(pSeries->getYValue(nPtIdx + n)); + } +} + +uno::Reference< beans::XPropertySet > OfPieDataSrc::getProps( + const VDataSeries* pSeries, sal_Int32 nPtIdx, + enum SubPieType eType) const +{ + const sal_Int32 n = pSeries->getTotalPointCount() - 3; + if (eType == SubPieType::LEFT) { + // nPtIdx should be in [0, n] + if (nPtIdx < n) { + return pSeries->getPropertiesOfPoint( nPtIdx ); + } else { + assert(nPtIdx == n); + // The aggregated wedge + // Not sure what to do here, but this isn't right. TODO + return pSeries->getPropertiesOfPoint(n); + } + } else { + assert(eType == SubPieType::RIGHT); + return pSeries->getPropertiesOfPoint(nPtIdx + n); + } +} + + } //namespace chart /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/PieChart.hxx b/chart2/source/view/charttypes/PieChart.hxx index 0517c87d67cd..8abd9973865c 100644 --- a/chart2/source/view/charttypes/PieChart.hxx +++ b/chart2/source/view/charttypes/PieChart.hxx @@ -26,6 +26,9 @@ #include <com/sun/star/awt/Point.hpp> #include <com/sun/star/chart2/PieChartSubType.hpp> +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + namespace chart { @@ -41,6 +44,75 @@ public: double m_fRingDistance; //>=0 m_fRingDistance=1 --> distance == width }; +enum class SubPieType { + NONE, + LEFT, + RIGHT +}; + + +//======================= +// class PieDataSrcBase +//======================= +class PieDataSrcBase +{ +public: + PieDataSrcBase() = default; + virtual ~PieDataSrcBase() = default; + + // Get number of points in the given pie subtype + virtual sal_Int32 getNPoints(const VDataSeries* pSeries, + enum SubPieType eType) const = 0; + + // Get the value for the given pie wedge, for the given subtype + virtual double getData(const VDataSeries* pSeries, sal_Int32 nPtIdx, + enum SubPieType eType) const = 0; + + // Get the properties for the wedge and subtype + virtual uno::Reference< beans::XPropertySet > getProps( + const VDataSeries* pSeries, sal_Int32 nPtIdx, + enum SubPieType eType) const = 0; +}; + +//======================= +// class PieDataSrc +//======================= +class PieDataSrc : public PieDataSrcBase +{ + sal_Int32 getNPoints(const VDataSeries* pSeries, + enum SubPieType eType) const; + + double getData(const VDataSeries* pSeries, sal_Int32 nPtIdx, + enum SubPieType eType) const; + + virtual uno::Reference< beans::XPropertySet > getProps( + const VDataSeries* pSeries, sal_Int32 nPtIdx, + enum SubPieType eType) const; +}; + +//======================= +// class OfPieDataSrc +//======================= +class OfPieDataSrc : public PieDataSrcBase +{ +public: + // Minimum sensible number of data points + static sal_Int32 minPoints() { return 4; } + + sal_Int32 getNPoints(const VDataSeries* pSeries, + enum SubPieType eType) const; + + double getData(const VDataSeries* pSeries, sal_Int32 nPtIdx, + enum SubPieType eType) const; + + virtual uno::Reference< beans::XPropertySet > getProps( + const VDataSeries* pSeries, sal_Int32 nPtIdx, + enum SubPieType eType) const; +}; + +//======================= +// class PieChart +//======================= class PieChart : public VSeriesPlotter { struct ShapeParam; @@ -75,12 +147,6 @@ public: virtual bool isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex ) override; virtual bool isSeparateStackingForDifferentSigns( sal_Int32 nDimensionIndex ) override; - enum class SubPieType { - NONE, - LEFT, - RIGHT - }; - private: //methods rtl::Reference<SvxShape> createDataPoint( @@ -135,6 +201,7 @@ struct PieLabelInfo; , const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget , const rtl::Reference<SvxShapeGroup>& xTextTarget , VDataSeries* pSeries + , const PieDataSrcBase *pDataSrc , sal_Int32 n3DRelativeHeight); private: //member @@ -168,6 +235,7 @@ private: //member double m_fMaxOffset; /// cached max offset value (init'ed to NaN) }; + } //namespace chart /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |