diff options
Diffstat (limited to 'chart2/source/view/charttypes')
-rw-r--r-- | chart2/source/view/charttypes/AreaChart.cxx | 67 | ||||
-rw-r--r-- | chart2/source/view/charttypes/AreaChart.hxx | 6 | ||||
-rw-r--r-- | chart2/source/view/charttypes/BarChart.cxx | 115 | ||||
-rw-r--r-- | chart2/source/view/charttypes/BarChart.hxx | 22 | ||||
-rw-r--r-- | chart2/source/view/charttypes/BarPositionHelper.cxx | 1 | ||||
-rw-r--r-- | chart2/source/view/charttypes/BubbleChart.cxx | 7 | ||||
-rw-r--r-- | chart2/source/view/charttypes/BubbleChart.hxx | 2 | ||||
-rw-r--r-- | chart2/source/view/charttypes/CandleStickChart.cxx | 37 | ||||
-rw-r--r-- | chart2/source/view/charttypes/CandleStickChart.hxx | 6 | ||||
-rw-r--r-- | chart2/source/view/charttypes/ConfigAccess.cxx | 74 | ||||
-rw-r--r-- | chart2/source/view/charttypes/NetChart.cxx | 26 | ||||
-rw-r--r-- | chart2/source/view/charttypes/NetChart.hxx | 6 | ||||
-rw-r--r-- | chart2/source/view/charttypes/PieChart.cxx | 1061 | ||||
-rw-r--r-- | chart2/source/view/charttypes/PieChart.hxx | 150 | ||||
-rw-r--r-- | chart2/source/view/charttypes/Splines.cxx | 89 | ||||
-rw-r--r-- | chart2/source/view/charttypes/VSeriesPlotter.cxx | 383 |
16 files changed, 1377 insertions, 675 deletions
diff --git a/chart2/source/view/charttypes/AreaChart.cxx b/chart2/source/view/charttypes/AreaChart.cxx index e5409ce5dff1..3bab48585148 100644 --- a/chart2/source/view/charttypes/AreaChart.cxx +++ b/chart2/source/view/charttypes/AreaChart.cxx @@ -24,25 +24,25 @@ #include <ExplicitCategoriesProvider.hxx> #include <ObjectIdentifier.hxx> #include "Splines.hxx" +#include <ChartType.hxx> #include <ChartTypeHelper.hxx> #include <LabelPositionHelper.hxx> #include <Clipping.hxx> #include <Stripe.hxx> #include <DateHelper.hxx> #include <unonames.hxx> -#include <ConfigAccess.hxx> #include <com/sun/star/chart2/Symbol.hpp> #include <com/sun/star/chart/DataLabelPlacement.hpp> #include <com/sun/star/chart/MissingValueTreatment.hpp> #include <sal/log.hxx> +#include <o3tl/safeint.hxx> #include <osl/diagnose.h> -#include <com/sun/star/drawing/DoubleSequence.hpp> -#include <com/sun/star/drawing/XShapes.hpp> #include <com/sun/star/beans/XPropertySet.hpp> #include <officecfg/Office/Compatibility.hxx> +#include <officecfg/Office/Chart.hxx> #include <limits> @@ -51,13 +51,12 @@ namespace chart using namespace ::com::sun::star; using namespace ::com::sun::star::chart2; -AreaChart::AreaChart( const uno::Reference<XChartType>& xChartTypeModel +AreaChart::AreaChart( const rtl::Reference<ChartType>& xChartTypeModel , sal_Int32 nDimensionCount , bool bCategoryXAxis , bool bNoArea ) : VSeriesPlotter( xChartTypeModel, nDimensionCount, bCategoryXAxis ) - , m_pMainPosHelper(new PlottingPositionHelper()) , m_bArea(!bNoArea) , m_bLine(bNoArea) , m_bSymbol( ChartTypeHelper::isSupportingSymbolProperties(xChartTypeModel,nDimensionCount) ) @@ -65,19 +64,19 @@ AreaChart::AreaChart( const uno::Reference<XChartType>& xChartTypeModel , m_nCurveResolution(20) , m_nSplineOrder(3) { + PlotterBase::m_pPosHelper = &m_aMainPosHelper; + VSeriesPlotter::m_pMainPosHelper = &m_aMainPosHelper; + m_pMainPosHelper->AllowShiftXAxisPos(true); m_pMainPosHelper->AllowShiftZAxisPos(true); - PlotterBase::m_pPosHelper = m_pMainPosHelper.get(); - VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper.get(); - try { - if( m_xChartTypeModelProps.is() ) + if( m_xChartTypeModel.is() ) { - m_xChartTypeModelProps->getPropertyValue(CHART_UNONAME_CURVE_STYLE) >>= m_eCurveStyle; - m_xChartTypeModelProps->getPropertyValue(CHART_UNONAME_CURVE_RESOLUTION) >>= m_nCurveResolution; - m_xChartTypeModelProps->getPropertyValue(CHART_UNONAME_SPLINE_ORDER) >>= m_nSplineOrder; + m_xChartTypeModel->getPropertyValue(CHART_UNONAME_CURVE_STYLE) >>= m_eCurveStyle; + m_xChartTypeModel->getPropertyValue(CHART_UNONAME_CURVE_RESOLUTION) >>= m_nCurveResolution; + m_xChartTypeModel->getPropertyValue(CHART_UNONAME_SPLINE_ORDER) >>= m_nSplineOrder; } } catch( uno::Exception& e ) @@ -456,26 +455,22 @@ bool AreaChart::impl_createArea( VDataSeries* pSeries pPosHelper->transformScaledLogicToScene( aPoly ); //create area: + rtl::Reference< SvxShape > xShape; if(m_nDimension==3) { - rtl::Reference< SvxShape > xShape = ShapeFactory::createArea3D( xSeriesGroupShape_Shapes + xShape = ShapeFactory::createArea3D( xSeriesGroupShape_Shapes , aPoly, getTransformedDepth() ); - PropertyMapper::setMappedProperties( *xShape - , pSeries->getPropertiesOfSeries() - , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); - //because of this name this line will be used for marking - ShapeFactory::setShapeName(xShape, "MarkHandles"); } else //m_nDimension!=3 { - SdrPathObj* pShape = ShapeFactory::createArea2D( xSeriesGroupShape_Shapes + xShape = ShapeFactory::createArea2D( xSeriesGroupShape_Shapes , aPoly ); - PropertyMapper::setPropertyNameMapForFilledSeriesProperties( - pShape - , pSeries->getPropertiesOfSeries()); - //because of this name this line will be used for marking - ShapeFactory::setShapeName(pShape, "MarkHandles"); } + PropertyMapper::setMappedProperties( *xShape + , pSeries->getPropertiesOfSeries() + , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); + //because of this name this line will be used for marking + ::chart::ShapeFactory::setShapeName(xShape, "MarkHandles"); return true; } @@ -639,6 +634,8 @@ void AreaChart::createShapes() } } + const bool bUseErrorRectangle = officecfg::Office::Chart::ErrorProperties::ErrorRectangle::get(); + sal_Int32 nZ=1; for( auto const& rZSlot : m_aZSlots ) { @@ -703,7 +700,7 @@ void AreaChart::createShapes() { std::vector<std::vector<css::drawing::Position3D>>& rPolygon = pSeries->m_aPolyPolygonShape3D; sal_Int32& rIndex = pSeries->m_nPolygonIndex; - if( 0<= rIndex && rIndex < static_cast<sal_Int32>(rPolygon.size()) ) + if( 0<= rIndex && o3tl::make_unsigned(rIndex) < rPolygon.size() ) { if( !rPolygon[ rIndex ].empty() ) rIndex++; //start a new polygon for the next point if the current poly is not empty @@ -797,13 +794,6 @@ void AreaChart::createShapes() !bCreateXErrorBar && !pSeries->getDataPointLabelIfLabel(nIndex) ) continue; - //create a group shape for this point and add to the series shape: - OUString aPointCID = ObjectIdentifier::createPointCID( - pSeries->getPointCID_Stub(), nIndex ); - rtl::Reference<SvxShapeGroupAnyD> xPointGroupShape_Shapes( - createGroupShape(xSeriesGroupShape_Shapes,aPointCID) ); - uno::Reference<drawing::XShape> xPointGroupShape_Shape( static_cast<cppu::OWeakObject*>(xPointGroupShape_Shapes.get()), uno::UNO_QUERY ); - { nCreatedPoints++; @@ -813,6 +803,13 @@ void AreaChart::createShapes() { if(m_nDimension!=3) { + //create a group shape for this point and add to the series shape: + OUString aPointCID = ObjectIdentifier::createPointCID( + pSeries->getPointCID_Stub(), nIndex ); + rtl::Reference<SvxShapeGroupAnyD> xPointGroupShape_Shapes; + if (pSymbolProperties->Style == SymbolStyle_STANDARD || pSymbolProperties->Style == SymbolStyle_GRAPHIC) + xPointGroupShape_Shapes = createGroupShape(xSeriesGroupShape_Shapes,aPointCID); + if (pSymbolProperties->Style != SymbolStyle_NONE) { aSymbolSize.DirectionX = pSymbolProperties->Size.Width; @@ -837,7 +834,7 @@ void AreaChart::createShapes() } } //create error bars or rectangles, depending on configuration - if ( ConfigAccess::getUseErrorRectangle() ) + if ( bUseErrorRectangle ) { if ( bCreateXErrorBar || bCreateYErrorBar ) { @@ -921,10 +918,6 @@ void AreaChart::createShapes() , rLogicYSumMap[nAttachedAxisIndex], aScreenPosition2D, eAlignment, nOffset ); } } - - //remove PointGroupShape if empty - if(!xPointGroupShape_Shapes->getCount()) - xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shape); } }//next series in x slot (next y slot) diff --git a/chart2/source/view/charttypes/AreaChart.hxx b/chart2/source/view/charttypes/AreaChart.hxx index 23d8adb0b4f1..411b9d6642aa 100644 --- a/chart2/source/view/charttypes/AreaChart.hxx +++ b/chart2/source/view/charttypes/AreaChart.hxx @@ -21,6 +21,7 @@ #include <memory> #include <VSeriesPlotter.hxx> +#include <PlottingPositionHelper.hxx> #include <com/sun/star/chart2/CurveStyle.hpp> namespace chart @@ -32,7 +33,7 @@ class AreaChart : public VSeriesPlotter public: AreaChart() = delete; - AreaChart( const css::uno::Reference< css::chart2::XChartType >& xChartTypeModel + AreaChart( const rtl::Reference< ::chart::ChartType >& xChartTypeModel , sal_Int32 nDimensionCount , bool bCategoryXAxis, bool bNoArea=false ); @@ -64,8 +65,7 @@ private: //methods , std::vector<std::vector<css::drawing::Position3D>> &aPoly ); private: //member - std::unique_ptr<PlottingPositionHelper> - m_pMainPosHelper; + PlottingPositionHelper m_aMainPosHelper; bool m_bArea;//false -> line or symbol only bool m_bLine; diff --git a/chart2/source/view/charttypes/BarChart.cxx b/chart2/source/view/charttypes/BarChart.cxx index 938355192e35..fcc969b898ad 100644 --- a/chart2/source/view/charttypes/BarChart.cxx +++ b/chart2/source/view/charttypes/BarChart.cxx @@ -20,6 +20,7 @@ #include "BarChart.hxx" #include "BarPositionHelper.hxx" +#include <ChartType.hxx> #include <ShapeFactory.hxx> #include <CommonConverters.hxx> #include <ObjectIdentifier.hxx> @@ -34,9 +35,7 @@ #include <com/sun/star/chart2/DataPointGeometry3D.hpp> #include <rtl/math.hxx> -#include <sal/log.hxx> -#include <tools/diagnose_ex.h> -#include <unordered_set> +#include <comphelper/diagnose_ex.hxx> namespace chart { @@ -44,20 +43,19 @@ using namespace ::com::sun::star; using namespace ::rtl::math; using namespace ::com::sun::star::chart2; -BarChart::BarChart( const uno::Reference<XChartType>& xChartTypeModel +BarChart::BarChart( const rtl::Reference<ChartType>& xChartTypeModel , sal_Int32 nDimensionCount ) : VSeriesPlotter( xChartTypeModel, nDimensionCount ) - , m_pMainPosHelper( new BarPositionHelper() ) { - PlotterBase::m_pPosHelper = m_pMainPosHelper.get(); - VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper.get(); + PlotterBase::m_pPosHelper = &m_aMainPosHelper; + VSeriesPlotter::m_pMainPosHelper = &m_aMainPosHelper; try { - if( m_xChartTypeModelProps.is() ) + if( m_xChartTypeModel.is() ) { - m_xChartTypeModelProps->getPropertyValue( "OverlapSequence" ) >>= m_aOverlapSequence; - m_xChartTypeModelProps->getPropertyValue( "GapwidthSequence" ) >>= m_aGapwidthSequence; + m_xChartTypeModel->getPropertyValue( "OverlapSequence" ) >>= m_aOverlapSequence; + m_xChartTypeModel->getPropertyValue( "GapwidthSequence" ) >>= m_aGapwidthSequence; } } catch( const uno::Exception& ) @@ -124,9 +122,7 @@ drawing::Direction3D BarChart::getPreferredDiagramAspectRatio() const } if( m_pMainPosHelper && m_pMainPosHelper->isSwapXAndY() ) { - double fTemp = aRet.DirectionX; - aRet.DirectionX = aRet.DirectionY; - aRet.DirectionY = fTemp; + std::swap(aRet.DirectionX, aRet.DirectionY); } } else @@ -450,8 +446,7 @@ void BarChart::createShapes() for (rtl::Reference<SvxShape> const & rShape : aShapeSet) { - E3dScene* pScene = dynamic_cast<E3dScene*>(rShape->GetSdrObject()); - if(nullptr != pScene) + if(E3dScene* pScene = DynCastE3dScene(rShape->GetSdrObject())) { aSceneSet.insert(pScene->getRootE3dSceneFromE3dObject()); } @@ -511,14 +506,14 @@ void BarChart::createShapes() { for( auto const& rZSlot : m_aZSlots ) { - BarPositionHelper* pPosHelper = m_pMainPosHelper.get(); + BarPositionHelper* pPosHelper = &m_aMainPosHelper; if( !rZSlot.empty() ) { sal_Int32 nAttachedAxisIndex = rZSlot.front().getAttachedAxisIndexForFirstSeries(); //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot pPosHelper = dynamic_cast<BarPositionHelper*>(&( getPlottingPositionHelper( nAttachedAxisIndex ) ) ); if(!pPosHelper) - pPosHelper = m_pMainPosHelper.get(); + pPosHelper = &m_aMainPosHelper; } PlotterBase::m_pPosHelper = pPosHelper; @@ -547,8 +542,6 @@ void BarChart::createShapes() getSeriesGroupShape(pSeries.get(), xSeriesTarget) ); rtl::Reference<SvxShapePolyPolygon> xShape( ShapeFactory::createLine2D( xSeriesGroupShape_Shapes, aPoly ) ); - PropertyMapper::setMappedProperties( *xShape, pSeries->getPropertiesOfSeries() - , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); } } } @@ -562,10 +555,10 @@ void BarChart::doZSlot( bool& bDrawConnectionLines, bool& bDrawConnectionLinesInited, const std::vector< VDataSeriesGroup >& rZSlot, const sal_Int32 nZ, const sal_Int32 nPointIndex, const sal_Int32 nStartIndex, - rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, - rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveTarget, - rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveEquationTarget, - rtl::Reference<SvxShapeGroupAnyD>& xTextTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveEquationTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xTextTarget, std::unordered_set<rtl::Reference<SvxShape>>& aShapeSet, std::map< VDataSeries*, FormerBarPoint >& aSeriesFormerPointMap, std::map< sal_Int32, double >& aLogicYSumMap) @@ -578,7 +571,7 @@ void BarChart::doZSlot( //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot BarPositionHelper* pPosHelper = dynamic_cast<BarPositionHelper*>(&( getPlottingPositionHelper( nAttachedAxisIndex ) ) ); if(!pPosHelper) - pPosHelper = m_pMainPosHelper.get(); + pPosHelper = &m_aMainPosHelper; PlotterBase::m_pPosHelper = pPosHelper; @@ -637,10 +630,10 @@ void BarChart::doXSlot( const VDataSeriesGroup& rXSlot, bool& bDrawConnectionLines, bool& bDrawConnectionLinesInited, const sal_Int32 nZ, const sal_Int32 nPointIndex, const sal_Int32 nStartIndex, - rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, - rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveTarget, - rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveEquationTarget, - rtl::Reference<SvxShapeGroupAnyD>& xTextTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveEquationTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xTextTarget, std::unordered_set<rtl::Reference<SvxShape>>& aShapeSet, std::map< VDataSeries*, FormerBarPoint >& aSeriesFormerPointMap, std::map< sal_Int32, double >& aLogicYSumMap, @@ -691,10 +684,10 @@ void BarChart::doXSlot( aShapeSet.insert(xSeriesGroupShape_Shapes); aShapeSet.insert(xSeriesBackgroundShape_Shapes); // Suspend setting rects dirty for the duration of this call - E3dScene* pScene = dynamic_cast<E3dScene*>(xSeriesGroupShape_Shapes->GetSdrObject()); + E3dScene* pScene = DynCastE3dScene(xSeriesGroupShape_Shapes->GetSdrObject()); if (pScene) pScene->SuspendReportingDirtyRects(); - pScene = dynamic_cast<E3dScene*>(xSeriesBackgroundShape_Shapes->GetSdrObject()); + pScene = DynCastE3dScene(xSeriesBackgroundShape_Shapes->GetSdrObject()); if (pScene) pScene->SuspendReportingDirtyRects(); @@ -858,6 +851,7 @@ void BarChart::doXSlot( //create partial point if( !approxEqual(fLowerYValue,fUpperYValue) ) { + rtl::Reference< SvxShape > xShape; if( m_nDimension==3 ) { drawing::Position3D aLogicBottom (fLogicX,fLogicYStart,fLogicZ); @@ -892,54 +886,41 @@ void BarChart::doXSlot( if( fTopHeight < 0 ) fTopHeight *= -1.0; - rtl::Reference< SvxShape > xShape = createDataPoint3D_Bar( + xShape = createDataPoint3D_Bar( xSeriesGroupShape_Shapes, aTransformedBottom, aSize, fTopHeight, nRotateZAngleHundredthDegree , xDataPointProperties, nGeometry3D ); - - if(bHasFillColorMapping) - { - double nPropVal = pSeries->getValueByProperty(nPointIndex, "FillColor"); - if(!std::isnan(nPropVal)) - { - xShape->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>(nPropVal))); - } - } - //set name/classified ObjectID (CID) - ShapeFactory::setShapeName(xShape - , ObjectIdentifier::createPointCID( - pSeries->getPointCID_Stub(),nPointIndex) ); } else //m_nDimension!=3 { - // performance improvement: alloc the sequence before the rendering - // otherwise we have 2 realloc calls - std::vector<std::vector<css::drawing::Position3D>> aPoly; - aPoly.resize(1); drawing::Position3D aLeftUpperPoint( fLogicX-fLogicBarWidth/2.0,fUpperYValue,fLogicZ ); drawing::Position3D aRightUpperPoint( fLogicX+fLogicBarWidth/2.0,fUpperYValue,fLogicZ ); - - AddPointToPoly( aPoly, drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) ); - AddPointToPoly( aPoly, drawing::Position3D( fLogicX+fLogicBarWidth/2.0,fLowerYValue,fLogicZ) ); - AddPointToPoly( aPoly, aRightUpperPoint ); - AddPointToPoly( aPoly, aLeftUpperPoint ); - AddPointToPoly( aPoly, drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) ); + std::vector<std::vector<css::drawing::Position3D>> aPoly + { + { // inner vector + drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ), + drawing::Position3D( fLogicX+fLogicBarWidth/2.0,fLowerYValue,fLogicZ), + aRightUpperPoint, + aLeftUpperPoint, + drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) + } + }; pPosHelper->transformScaledLogicToScene( aPoly ); - std::optional<sal_Int32> xFillColor; - if(bHasFillColorMapping) + xShape = ShapeFactory::createArea2D( xSeriesGroupShape_Shapes, aPoly ); + PropertyMapper::setMappedProperties( *xShape, xDataPointProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); + } + + if(bHasFillColorMapping) + { + double nPropVal = pSeries->getValueByProperty(nPointIndex, "FillColor"); + if(!std::isnan(nPropVal)) { - double nPropVal = pSeries->getValueByProperty(nPointIndex, "FillColor"); - if(!std::isnan(nPropVal)) - xFillColor = static_cast<sal_Int32>(nPropVal); + xShape->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>(nPropVal))); } - SdrPathObj* pShape = ShapeFactory::createArea2D( xSeriesGroupShape_Shapes, aPoly, /*bSetZOrderToZero*/false ); - PropertyMapper::setPropertyNameMapForFilledSeriesProperties(pShape, xDataPointProperties, xFillColor); - - //set name/classified ObjectID (CID) - ShapeFactory::setShapeName(pShape - , ObjectIdentifier::createPointCID( - pSeries->getPointCID_Stub(),nPointIndex) ); } - + //set name/classified ObjectID (CID) + ShapeFactory::setShapeName(xShape + , ObjectIdentifier::createPointCID( + pSeries->getPointCID_Stub(),nPointIndex) ); } //create error bar diff --git a/chart2/source/view/charttypes/BarChart.hxx b/chart2/source/view/charttypes/BarChart.hxx index 5e9ab5e49f48..ff46786afcbb 100644 --- a/chart2/source/view/charttypes/BarChart.hxx +++ b/chart2/source/view/charttypes/BarChart.hxx @@ -21,10 +21,10 @@ #include <memory> #include <VSeriesPlotter.hxx> +#include "BarPositionHelper.hxx" namespace chart { -class BarPositionHelper; class BarChart : public VSeriesPlotter { @@ -32,7 +32,7 @@ class BarChart : public VSeriesPlotter public: BarChart() = delete; - BarChart( const css::uno::Reference< css::chart2::XChartType >& xChartTypeModel + BarChart( const rtl::Reference< ::chart::ChartType >& xChartTypeModel , sal_Int32 nDimensionCount ); virtual ~BarChart() override; @@ -84,10 +84,10 @@ private: //methods void doZSlot( bool& bDrawConnectionLines, bool& bDrawConnectionLinesInited, const std::vector< VDataSeriesGroup >& rZSlot, sal_Int32 nZ, sal_Int32 nPointIndex, sal_Int32 nStartIndex, - rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, - rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveTarget, - rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveEquationTarget, - rtl::Reference<SvxShapeGroupAnyD>& xTextTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveEquationTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xTextTarget, std::unordered_set<rtl::Reference<SvxShape>>& aShapeSet, std::map< VDataSeries*, FormerBarPoint >& aSeriesFormerPointMap, std::map< sal_Int32, double >& aLogicYSumMap); @@ -96,10 +96,10 @@ private: //methods const VDataSeriesGroup& rXSlot, bool& bDrawConnectionLines, bool& bDrawConnectionLinesInited, sal_Int32 nZ, sal_Int32 nPointIndex, sal_Int32 nStartIndex, - rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, - rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveTarget, - rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveEquationTarget, - rtl::Reference<SvxShapeGroupAnyD>& xTextTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xRegressionCurveEquationTarget, + const rtl::Reference<SvxShapeGroupAnyD>& xTextTarget, std::unordered_set<rtl::Reference<SvxShape>>& aShapeSet, std::map< VDataSeries*, FormerBarPoint >& aSeriesFormerPointMap, std::map< sal_Int32, double >& aLogicYSumMap, @@ -109,7 +109,7 @@ private: //methods sal_Int32 nAttachedAxisIndex); private: //member - std::unique_ptr<BarPositionHelper> m_pMainPosHelper; + BarPositionHelper m_aMainPosHelper; css::uno::Sequence< sal_Int32 > m_aOverlapSequence; css::uno::Sequence< sal_Int32 > m_aGapwidthSequence; }; diff --git a/chart2/source/view/charttypes/BarPositionHelper.cxx b/chart2/source/view/charttypes/BarPositionHelper.cxx index f8ac3e7d62ef..279d998a02e4 100644 --- a/chart2/source/view/charttypes/BarPositionHelper.cxx +++ b/chart2/source/view/charttypes/BarPositionHelper.cxx @@ -23,7 +23,6 @@ namespace chart { using namespace ::com::sun::star; -using namespace ::com::sun::star::chart2; BarPositionHelper::BarPositionHelper() : CategoryPositionHelper( 1 ) diff --git a/chart2/source/view/charttypes/BubbleChart.cxx b/chart2/source/view/charttypes/BubbleChart.cxx index fc7fcdd2597b..d5bd2c3a16fc 100644 --- a/chart2/source/view/charttypes/BubbleChart.cxx +++ b/chart2/source/view/charttypes/BubbleChart.cxx @@ -22,6 +22,7 @@ #include <ShapeFactory.hxx> #include <ObjectIdentifier.hxx> #include <LabelPositionHelper.hxx> +#include <ChartType.hxx> #include <com/sun/star/chart/DataLabelPlacement.hpp> #include <sal/log.hxx> @@ -32,9 +33,8 @@ namespace chart { using namespace ::com::sun::star; -using namespace ::com::sun::star::chart2; -BubbleChart::BubbleChart( const uno::Reference<XChartType>& xChartTypeModel +BubbleChart::BubbleChart( const rtl::Reference<ChartType>& xChartTypeModel , sal_Int32 nDimensionCount ) : VSeriesPlotter( xChartTypeModel, nDimensionCount, false ) , m_fMaxLogicBubbleSize( 0.0 ) @@ -261,7 +261,6 @@ void BubbleChart::createShapes() pSeries->getPointCID_Stub(), nIndex ); rtl::Reference<SvxShapeGroupAnyD> xPointGroupShape_Shapes( createGroupShape(xSeriesGroupShape_Shapes,aPointCID) ); - uno::Reference<drawing::XShape> xPointGroupShape_Shape = xPointGroupShape_Shapes; { nCreatedPoints++; @@ -345,7 +344,7 @@ void BubbleChart::createShapes() //remove PointGroupShape if empty if(!xPointGroupShape_Shapes->getCount()) - xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shape); + xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shapes); }//next series in x slot (next y slot) }//next x slot diff --git a/chart2/source/view/charttypes/BubbleChart.hxx b/chart2/source/view/charttypes/BubbleChart.hxx index 06d1d1200340..c25e5b6345bf 100644 --- a/chart2/source/view/charttypes/BubbleChart.hxx +++ b/chart2/source/view/charttypes/BubbleChart.hxx @@ -30,7 +30,7 @@ class BubbleChart : public VSeriesPlotter public: BubbleChart() = delete; - BubbleChart( const css::uno::Reference< css::chart2::XChartType >& xChartTypeModel + BubbleChart( const rtl::Reference< ::chart::ChartType >& xChartTypeModel , sal_Int32 nDimensionCount ); virtual ~BubbleChart() override; diff --git a/chart2/source/view/charttypes/CandleStickChart.cxx b/chart2/source/view/charttypes/CandleStickChart.cxx index 44fcd600bd49..5f3a19582e12 100644 --- a/chart2/source/view/charttypes/CandleStickChart.cxx +++ b/chart2/source/view/charttypes/CandleStickChart.cxx @@ -18,6 +18,7 @@ */ #include "CandleStickChart.hxx" +#include <ChartType.hxx> #include <ShapeFactory.hxx> #include <CommonConverters.hxx> #include <ExplicitCategoriesProvider.hxx> @@ -25,21 +26,19 @@ #include "BarPositionHelper.hxx" #include <DateHelper.hxx> #include <com/sun/star/beans/XPropertySet.hpp> -#include <tools/diagnose_ex.h> +#include <comphelper/diagnose_ex.hxx> #include <osl/diagnose.h> namespace chart { using namespace ::com::sun::star; -using namespace ::com::sun::star::chart2; -CandleStickChart::CandleStickChart( const uno::Reference<XChartType>& xChartTypeModel +CandleStickChart::CandleStickChart( const rtl::Reference<ChartType>& xChartTypeModel , sal_Int32 nDimensionCount ) : VSeriesPlotter( xChartTypeModel, nDimensionCount ) - , m_pMainPosHelper( new BarPositionHelper() ) { - PlotterBase::m_pPosHelper = m_pMainPosHelper.get(); - VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper.get(); + PlotterBase::m_pPosHelper = &m_aMainPosHelper; + VSeriesPlotter::m_pMainPosHelper = &m_aMainPosHelper; } CandleStickChart::~CandleStickChart() @@ -104,15 +103,15 @@ void CandleStickChart::createShapes() tAnySequence aWhiteBox_Values, aBlackBox_Values; try { - if( m_xChartTypeModelProps.is() ) + if( m_xChartTypeModel.is() ) { - m_xChartTypeModelProps->getPropertyValue( "ShowFirst" ) >>= bShowFirst; + m_xChartTypeModel->getPropertyValue( "ShowFirst" ) >>= bShowFirst; uno::Reference< beans::XPropertySet > xWhiteDayProps; uno::Reference< beans::XPropertySet > xBlackDayProps; - m_xChartTypeModelProps->getPropertyValue( "Japanese" ) >>= bJapaneseStyle; - m_xChartTypeModelProps->getPropertyValue( "WhiteDay" ) >>= xWhiteDayProps; - m_xChartTypeModelProps->getPropertyValue( "BlackDay" ) >>= xBlackDayProps; + m_xChartTypeModel->getPropertyValue( "Japanese" ) >>= bJapaneseStyle; + m_xChartTypeModel->getPropertyValue( "WhiteDay" ) >>= xWhiteDayProps; + m_xChartTypeModel->getPropertyValue( "BlackDay" ) >>= xBlackDayProps; tPropertyNameValueMap aWhiteBox_Map; PropertyMapper::getValueMap( aWhiteBox_Map, PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xWhiteDayProps ); @@ -136,14 +135,14 @@ void CandleStickChart::createShapes() { for( auto const& rZSlot : m_aZSlots ) { - BarPositionHelper* pPosHelper = m_pMainPosHelper.get(); + BarPositionHelper* pPosHelper = &m_aMainPosHelper; if( !rZSlot.empty() ) { sal_Int32 nAttachedAxisIndex = rZSlot.front().getAttachedAxisIndexForFirstSeries(); //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot pPosHelper = dynamic_cast<BarPositionHelper*>(&( getPlottingPositionHelper( nAttachedAxisIndex ) ) ); if(!pPosHelper) - pPosHelper = m_pMainPosHelper.get(); + pPosHelper = &m_aMainPosHelper; } PlotterBase::m_pPosHelper = pPosHelper; @@ -200,9 +199,7 @@ void CandleStickChart::createShapes() drawing::Position3D aPosMiddleMinimum( pPosHelper->transformScaledLogicToScene( fScaledX, fScaledY_Min ,0 ,true ) ); drawing::Position3D aPosMiddleMaximum( pPosHelper->transformScaledLogicToScene( fScaledX, fScaledY_Max ,0 ,true ) ); - rtl::Reference<SvxShapeGroupAnyD> xLossGainTarget( xGainTarget ); - if(bBlack) - xLossGainTarget = xLossTarget; + rtl::Reference<SvxShapeGroupAnyD> xLossGainTarget(bBlack ? xLossTarget : xGainTarget); uno::Reference< beans::XPropertySet > xPointProp( pSeries->getPropertiesOfPoint( nIndex )); rtl::Reference<SvxShapeGroupAnyD> xPointGroupShape_Shapes; @@ -215,10 +212,10 @@ void CandleStickChart::createShapes() //create min-max line if( isValidPosition(aPosMiddleMinimum) && isValidPosition(aPosMiddleMaximum) ) { - std::vector<std::vector<css::drawing::Position3D>> aPoly; - sal_Int32 nLineIndex =0; - AddPointToPoly( aPoly, aPosMiddleMinimum, nLineIndex); - AddPointToPoly( aPoly, aPosMiddleMaximum, nLineIndex); + std::vector<std::vector<css::drawing::Position3D>> aPoly + { + { aPosMiddleMinimum, aPosMiddleMaximum } + }; rtl::Reference<SvxShapePolyPolygon> xShape = ShapeFactory::createLine2D( xPointGroupShape_Shapes, aPoly); diff --git a/chart2/source/view/charttypes/CandleStickChart.hxx b/chart2/source/view/charttypes/CandleStickChart.hxx index eddee6b72e12..3202cf0d7eaf 100644 --- a/chart2/source/view/charttypes/CandleStickChart.hxx +++ b/chart2/source/view/charttypes/CandleStickChart.hxx @@ -20,11 +20,11 @@ #pragma once #include <memory> +#include "BarPositionHelper.hxx" #include <VSeriesPlotter.hxx> namespace chart { -class BarPositionHelper; class CandleStickChart : public VSeriesPlotter { @@ -32,7 +32,7 @@ class CandleStickChart : public VSeriesPlotter public: CandleStickChart() = delete; - CandleStickChart( const css::uno::Reference< css::chart2::XChartType >& xChartTypeModel + CandleStickChart( const rtl::Reference< ::chart::ChartType >& xChartTypeModel , sal_Int32 nDimensionCount ); virtual ~CandleStickChart() override; @@ -47,7 +47,7 @@ public: virtual LegendSymbolStyle getLegendSymbolStyle() override; private: //member - std::unique_ptr<BarPositionHelper> m_pMainPosHelper; + BarPositionHelper m_aMainPosHelper; }; } //namespace chart diff --git a/chart2/source/view/charttypes/ConfigAccess.cxx b/chart2/source/view/charttypes/ConfigAccess.cxx deleted file mode 100644 index ce02c4817e0a..000000000000 --- a/chart2/source/view/charttypes/ConfigAccess.cxx +++ /dev/null @@ -1,74 +0,0 @@ -/* -*- 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 <ConfigAccess.hxx> - -#include <unotools/configitem.hxx> -#include <o3tl/any.hxx> -#include <com/sun/star/uno/Sequence.hxx> - -namespace chart -{ -using namespace ::com::sun::star; - -namespace -{ -class ChartConfigItem : public ::utl::ConfigItem -{ -private: - virtual void ImplCommit() override; - -public: - ChartConfigItem(); - - bool getUseErrorRectangle(); - virtual void Notify(const uno::Sequence<OUString>& aPropertyNames) override; -}; -} - -ChartConfigItem::ChartConfigItem() - : ConfigItem("Office.Chart/ErrorProperties") -{ -} - -void ChartConfigItem::ImplCommit() {} -void ChartConfigItem::Notify(const uno::Sequence<OUString>&) {} - -bool ChartConfigItem::getUseErrorRectangle() -{ - uno::Sequence<OUString> aNames{ "ErrorRectangle" }; - - auto b = o3tl::tryAccess<bool>(GetProperties(aNames)[0]); - return b && *b; -} - -namespace ConfigAccess -{ -bool getUseErrorRectangle() -{ - //a ChartConfigItem Singleton - static ChartConfigItem SINGLETON; - bool bResult(SINGLETON.getUseErrorRectangle()); - return bResult; -} -} //namespace ConfigAccess - -} //namespace chart - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/NetChart.cxx b/chart2/source/view/charttypes/NetChart.cxx index 5507e30e0579..5b8f1db34be5 100644 --- a/chart2/source/view/charttypes/NetChart.cxx +++ b/chart2/source/view/charttypes/NetChart.cxx @@ -27,14 +27,15 @@ #include <Clipping.hxx> #include <PolarLabelPositionHelper.hxx> #include <DateHelper.hxx> +#include <ChartType.hxx> #include <com/sun/star/chart2/Symbol.hpp> #include <com/sun/star/chart/DataLabelPlacement.hpp> #include <com/sun/star/chart/MissingValueTreatment.hpp> +#include <o3tl/safeint.hxx> #include <osl/diagnose.h> -#include <com/sun/star/drawing/XShapes.hpp> #include <officecfg/Office/Compatibility.hxx> #include <limits> @@ -44,7 +45,7 @@ namespace chart using namespace ::com::sun::star; using namespace ::com::sun::star::chart2; -NetChart::NetChart( const uno::Reference<XChartType>& xChartTypeModel +NetChart::NetChart( const rtl::Reference<ChartType>& xChartTypeModel , sal_Int32 nDimensionCount , bool bNoArea , std::unique_ptr<PlottingPositionHelper> pPlottingPositionHelper @@ -111,7 +112,7 @@ drawing::Direction3D NetChart::getPreferredDiagramAspectRatio() const } bool NetChart::impl_createLine( VDataSeries* pSeries - , std::vector<std::vector<css::drawing::Position3D>>* pSeriesPoly + , const std::vector<std::vector<css::drawing::Position3D>>* pSeriesPoly , PlottingPositionHelper const * pPosHelper ) { //return true if a line was created successfully @@ -163,7 +164,7 @@ bool NetChart::impl_createLine( VDataSeries* pSeries } bool NetChart::impl_createArea( VDataSeries* pSeries - , std::vector<std::vector<css::drawing::Position3D>>* pSeriesPoly + , const std::vector<std::vector<css::drawing::Position3D>>* pSeriesPoly , std::vector<std::vector<css::drawing::Position3D>> const * pPreviousSeriesPoly , PlottingPositionHelper const * pPosHelper ) { @@ -221,12 +222,14 @@ bool NetChart::impl_createArea( VDataSeries* pSeries pPosHelper->transformScaledLogicToScene( aPoly ); //create area: - SdrPathObj* pShape = ShapeFactory::createArea2D( xSeriesGroupShape_Shapes - , aPoly ); - PropertyMapper::setPropertyNameMapForFilledSeriesProperties(pShape - , pSeries->getPropertiesOfSeries() ); + rtl::Reference<SvxShapePolyPolygon> + xShape = ShapeFactory::createArea2D( xSeriesGroupShape_Shapes + , aPoly ); + PropertyMapper::setMappedProperties( *xShape + , pSeries->getPropertiesOfSeries() + , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); //because of this name this line will be used for marking - ::chart::ShapeFactory::setShapeName(pShape, "MarkHandles"); + ::chart::ShapeFactory::setShapeName(xShape, "MarkHandles"); return true; } @@ -438,7 +441,7 @@ void NetChart::createShapes() { std::vector<std::vector<css::drawing::Position3D>>& rPolygon = pSeries->m_aPolyPolygonShape3D; sal_Int32& rIndex = pSeries->m_nPolygonIndex; - if( 0<= rIndex && rIndex < static_cast<sal_Int32>(rPolygon.size()) ) + if( 0<= rIndex && o3tl::make_unsigned(rIndex) < rPolygon.size() ) { if( !rPolygon[ rIndex ].empty() ) rIndex++; //start a new polygon for the next point if the current poly is not empty @@ -526,7 +529,6 @@ void NetChart::createShapes() pSeries->getPointCID_Stub(), nIndex ); rtl::Reference<SvxShapeGroupAnyD> xPointGroupShape_Shapes( createGroupShape(xSeriesGroupShape_Shapes,aPointCID) ); - uno::Reference<drawing::XShape> xPointGroupShape_Shape( xPointGroupShape_Shapes ); { //create data point @@ -624,7 +626,7 @@ void NetChart::createShapes() //remove PointGroupShape if empty if(!xPointGroupShape_Shapes->getCount()) - xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shape); + xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shapes); }//next series in x slot (next y slot) }//next x slot diff --git a/chart2/source/view/charttypes/NetChart.hxx b/chart2/source/view/charttypes/NetChart.hxx index 9883ba118ac2..a536daf15fd2 100644 --- a/chart2/source/view/charttypes/NetChart.hxx +++ b/chart2/source/view/charttypes/NetChart.hxx @@ -31,7 +31,7 @@ class NetChart : public VSeriesPlotter public: NetChart() = delete; - NetChart( const css::uno::Reference< css::chart2::XChartType >& xChartTypeModel + NetChart( const rtl::Reference< ::chart::ChartType >& xChartTypeModel , sal_Int32 nDimensionCount , bool bNoArea , std::unique_ptr<PlottingPositionHelper> pPlottingPositionHelper //takes ownership @@ -53,11 +53,11 @@ public: private: //methods void impl_createSeriesShapes(); bool impl_createArea( VDataSeries* pSeries - , std::vector<std::vector<css::drawing::Position3D>>* pSeriesPoly + , const std::vector<std::vector<css::drawing::Position3D>>* pSeriesPoly , std::vector<std::vector<css::drawing::Position3D>> const * pPreviousSeriesPoly , PlottingPositionHelper const * pPosHelper ); bool impl_createLine( VDataSeries* pSeries - , std::vector<std::vector<css::drawing::Position3D>>* pSeriesPoly + , const std::vector<std::vector<css::drawing::Position3D>>* pSeriesPoly , PlottingPositionHelper const * pPosHelper ); private: //member diff --git a/chart2/source/view/charttypes/PieChart.cxx b/chart2/source/view/charttypes/PieChart.cxx index 2fd709500d29..ff8cf62f133a 100644 --- a/chart2/source/view/charttypes/PieChart.cxx +++ b/chart2/source/view/charttypes/PieChart.cxx @@ -20,23 +20,21 @@ #include <BaseGFXHelper.hxx> #include <VLineProperties.hxx> #include "PieChart.hxx" -#include <PlottingPositionHelper.hxx> #include <ShapeFactory.hxx> #include <PolarLabelPositionHelper.hxx> #include <CommonConverters.hxx> #include <ObjectIdentifier.hxx> +#include <ChartType.hxx> +#include <DataSeries.hxx> +#include <DataSeriesProperties.hxx> #include <com/sun/star/chart/DataLabelPlacement.hpp> -#include <com/sun/star/chart2/XChartType.hpp> #include <com/sun/star/chart2/XColorScheme.hpp> -#include <com/sun/star/container/XChild.hpp> -#include <com/sun/star/drawing/XShape.hpp> #include <com/sun/star/drawing/XShapes.hpp> -#include <com/sun/star/beans/XPropertySet.hpp> #include <sal/log.hxx> #include <osl/diagnose.h> -#include <tools/diagnose_ex.h> +#include <comphelper/diagnose_ex.hxx> #include <tools/helpers.hxx> #include <limits> @@ -44,6 +42,7 @@ using namespace ::com::sun::star; using namespace ::com::sun::star::chart2; +using namespace ::chart::DataSeriesProperties; namespace chart { @@ -104,7 +103,7 @@ struct PieChart::ShapeParam namespace { -::basegfx::B2IRectangle lcl_getRect(const uno::Reference<drawing::XShape>& xShape) +::basegfx::B2IRectangle lcl_getRect(const rtl::Reference<SvxShape>& xShape) { ::basegfx::B2IRectangle aRect; if (xShape.is()) @@ -125,18 +124,6 @@ bool lcl_isInsidePage(const awt::Point& rPos, const awt::Size& rSize, const awt: } //end anonymous namespace -class PiePositionHelper : public PolarPlottingPositionHelper -{ -public: - PiePositionHelper( double fAngleDegreeOffset ); - - bool getInnerAndOuterRadius( double fCategoryX, double& fLogicInnerRadius, double& fLogicOuterRadius, bool bUseRings, double fMaxOffset ) const; - -public: - //Distance between different category rings, seen relative to width of a ring: - double m_fRingDistance; //>=0 m_fRingDistance=1 --> distance == width -}; - PiePositionHelper::PiePositionHelper( double fAngleDegreeOffset ) : m_fRingDistance(0.0) { @@ -191,38 +178,54 @@ bool PiePositionHelper::getInnerAndOuterRadius( double fCategoryX return true; } -PieChart::PieChart( const uno::Reference<XChartType>& xChartTypeModel + +bool PiePositionHelper::clockwiseWedges() const +{ + const ExplicitScaleData& rAngleScale = m_bSwapXAndY ? m_aScales[1] : m_aScales[0]; + return rAngleScale.Orientation == AxisOrientation_REVERSE; +} + + +PieChart::PieChart( const rtl::Reference<ChartType>& xChartTypeModel , sal_Int32 nDimensionCount , bool bExcludingPositioning ) : VSeriesPlotter( xChartTypeModel, nDimensionCount ) - , m_pPosHelper( new PiePositionHelper( (m_nDimension==3) ? 0.0 : 90.0 ) ) + , m_aPosHelper( (m_nDimension==3) ? 0.0 : 90.0 ) , m_bUseRings(false) , m_bSizeExcludesLabelsAndExplodedSegments(bExcludingPositioning) + , m_eSubType(PieChartSubType_NONE) , m_fMaxOffset(std::numeric_limits<double>::quiet_NaN()) { - PlotterBase::m_pPosHelper = m_pPosHelper.get(); - VSeriesPlotter::m_pMainPosHelper = m_pPosHelper.get(); - m_pPosHelper->m_fRadiusOffset = 0.0; - m_pPosHelper->m_fRingDistance = 0.0; + PlotterBase::m_pPosHelper = &m_aPosHelper; + VSeriesPlotter::m_pMainPosHelper = &m_aPosHelper; + m_aPosHelper.m_fRadiusOffset = 0.0; + m_aPosHelper.m_fRingDistance = 0.0; - uno::Reference< beans::XPropertySet > xChartTypeProps( xChartTypeModel, uno::UNO_QUERY ); - if( !xChartTypeProps.is() ) + if( !xChartTypeModel.is() ) return; try { - xChartTypeProps->getPropertyValue( "UseRings") >>= m_bUseRings; + xChartTypeModel->getFastPropertyValue(PROP_PIECHARTTYPE_USE_RINGS) >>= m_bUseRings; // "UseRings" if( m_bUseRings ) { - m_pPosHelper->m_fRadiusOffset = 1.0; + m_aPosHelper.m_fRadiusOffset = 1.0; if( nDimensionCount==3 ) - m_pPosHelper->m_fRingDistance = 0.1; + m_aPosHelper.m_fRingDistance = 0.1; } } catch( const uno::Exception& ) { TOOLS_WARN_EXCEPTION("chart2", "" ); } + try + { + xChartTypeModel->getFastPropertyValue(PROP_PIECHARTTYPE_SUBTYPE) >>= m_eSubType; // "SubType" + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } } PieChart::~PieChart() @@ -232,7 +235,7 @@ PieChart::~PieChart() void PieChart::setScales( std::vector< ExplicitScaleData >&& rScales, bool /* bSwapXAndYAxis */ ) { OSL_ENSURE(m_nDimension<=static_cast<sal_Int32>(rScales.size()),"Dimension of Plotter does not fit two dimension of given scale sequence"); - m_pPosHelper->setScales( std::move(rScales), true ); + m_aPosHelper.setScales( std::move(rScales), true ); } drawing::Direction3D PieChart::getPreferredDiagramAspectRatio() const @@ -248,42 +251,140 @@ bool PieChart::shouldSnapRectToUsedArea() } rtl::Reference<SvxShape> PieChart::createDataPoint( + const SubPieType e_subType, const rtl::Reference<SvxShapeGroupAnyD>& xTarget, const uno::Reference<beans::XPropertySet>& xObjectProperties, - const ShapeParam& rParam ) + const ShapeParam& rParam, + const sal_Int32 nPointCount, + const bool bConcentricExplosion) { //transform position: drawing::Direction3D aOffset; - if (rParam.mfExplodePercentage != 0.0) - { - double fAngle = rParam.mfUnitCircleStartAngleDegree + rParam.mfUnitCircleWidthAngleDegree/2.0; - double fRadius = (rParam.mfUnitCircleOuterRadius-rParam.mfUnitCircleInnerRadius)*rParam.mfExplodePercentage; - drawing::Position3D aOrigin = m_pPosHelper->transformUnitCircleToScene(0, 0, rParam.mfLogicZ); - drawing::Position3D aNewOrigin = m_pPosHelper->transformUnitCircleToScene(fAngle, fRadius, rParam.mfLogicZ); - aOffset = aNewOrigin - aOrigin; + double fExplodedInnerRadius = rParam.mfUnitCircleInnerRadius; + double fExplodedOuterRadius = rParam.mfUnitCircleOuterRadius; + double fStartAngle = rParam.mfUnitCircleStartAngleDegree; + double fWidthAngle = rParam.mfUnitCircleWidthAngleDegree; + + if (rParam.mfExplodePercentage != 0.0) { + double fRadius = (fExplodedOuterRadius-fExplodedInnerRadius)*rParam.mfExplodePercentage; + + if (bConcentricExplosion) { + + // For concentric explosion, increase the radius but retain the original + // arc length of all ring segments together. This results in a gap + // that's evenly divided among all segments, assuming they all have + // the same explosion percentage + assert(fExplodedInnerRadius >= 0 && fExplodedOuterRadius > 0); + double fAngleRatio = (fExplodedInnerRadius + fExplodedOuterRadius) / + (fExplodedInnerRadius + fExplodedOuterRadius + 2 * fRadius); + + assert(nPointCount > 0); + double fAngleGap = 360 * (1.0 - fAngleRatio) / nPointCount; + fStartAngle += fAngleGap / 2; + fWidthAngle -= fAngleGap; + + fExplodedInnerRadius += fRadius; + fExplodedOuterRadius += fRadius; + + } else { + // For the non-concentric explosion case, keep the original radius + // but shift the circle origin + double fAngle = fStartAngle + fWidthAngle/2.0; + + drawing::Position3D aOrigin = m_aPosHelper.transformUnitCircleToScene(0, 0, rParam.mfLogicZ); + drawing::Position3D aNewOrigin = m_aPosHelper.transformUnitCircleToScene(fAngle, fRadius, rParam.mfLogicZ); + aOffset = aNewOrigin - aOrigin; + } + } else { + drawing::Position3D aOrigin, aNewOrigin; + switch (e_subType) { + case SubPieType::LEFT: + // Draw the main pie for bar-of-pie/pie-of-pie smaller and to the left + aOrigin = m_aPosHelper.transformUnitCircleToScene(0, 0, rParam.mfLogicZ); + aNewOrigin = m_aPosHelper.transformUnitCircleToScene(180, 0.75, rParam.mfLogicZ); + aOffset = aNewOrigin - aOrigin; + fExplodedOuterRadius *= 2.0/3; + break; + case SubPieType::RIGHT: + // Draw the sub-pie for pie-of-pie much smaller and to the right + aOrigin = m_aPosHelper.transformUnitCircleToScene(0, 0, rParam.mfLogicZ); + aNewOrigin = m_aPosHelper.transformUnitCircleToScene(0, 0.75, rParam.mfLogicZ); + aOffset = aNewOrigin - aOrigin; + fExplodedOuterRadius *= 1.0/3; + break; + case SubPieType::NONE: + default: + // no change + break; + } } + //create point rtl::Reference<SvxShape> xShape; if(m_nDimension==3) { xShape = ShapeFactory::createPieSegment( xTarget - , rParam.mfUnitCircleStartAngleDegree, rParam.mfUnitCircleWidthAngleDegree - , rParam.mfUnitCircleInnerRadius, rParam.mfUnitCircleOuterRadius - , aOffset, B3DHomMatrixToHomogenMatrix( m_pPosHelper->getUnitCartesianToScene() ) + , fStartAngle, fWidthAngle + , fExplodedInnerRadius, fExplodedOuterRadius + , aOffset, B3DHomMatrixToHomogenMatrix( m_aPosHelper.getUnitCartesianToScene() ) , rParam.mfDepth ); } else { xShape = ShapeFactory::createPieSegment2D( xTarget - , rParam.mfUnitCircleStartAngleDegree, rParam.mfUnitCircleWidthAngleDegree - , rParam.mfUnitCircleInnerRadius, rParam.mfUnitCircleOuterRadius - , aOffset, B3DHomMatrixToHomogenMatrix( m_pPosHelper->getUnitCartesianToScene() ) ); + , fStartAngle, fWidthAngle + , fExplodedInnerRadius, fExplodedOuterRadius + , aOffset, B3DHomMatrixToHomogenMatrix( m_aPosHelper.getUnitCartesianToScene() ) ); } PropertyMapper::setMappedProperties( *xShape, xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); return xShape; } +rtl::Reference<SvxShape> PieChart::createBarDataPoint( + const rtl::Reference<SvxShapeGroupAnyD>& xTarget, + const uno::Reference<beans::XPropertySet>& xObjectProperties, + const ShapeParam& rParam, + double fBarSegBottom, double fBarSegTop) +{ + drawing::Position3D aP0, aP1; + + // Draw the bar for bar-of-pie small and to the right. Width and + // position are hard-coded for now. + +#if 0 + aP0 = cartesianPosHelper.transformLogicToScene(0.75, fBarSegBottom, + rParam.mfLogicZ, false); + aP1 = cartesianPosHelper.transformLogicToScene(1.25, fBarSegTop, + rParam.mfLogicZ, false); +#else + double x0 = m_aPosHelper.transformUnitCircleToScene(0, 0.75, 0).PositionX; + double x1 = m_aPosHelper.transformUnitCircleToScene(0, 1.25, 0).PositionX; + double y0 = m_aPosHelper.transformUnitCircleToScene( + 90, fBarSegBottom, 0).PositionY; + double y1 = m_aPosHelper.transformUnitCircleToScene( + 90, fBarSegTop, 0).PositionY; + + aP0 = drawing::Position3D(x0, y0, rParam.mfLogicZ); + aP1 = drawing::Position3D(x1, y1, rParam.mfLogicZ); +#endif + + const css::awt::Point aPos(aP0.PositionX, aP1.PositionY); + const css::awt::Size aSz(fabs(aP0.PositionX - aP1.PositionX), + fabs(aP0.PositionY - aP1.PositionY)); + + const tNameSequence emptyNameSeq; + const tAnySequence emptyValSeq; + //create point + rtl::Reference<SvxShape> xShape = ShapeFactory::createRectangle( + xTarget, + aSz, aPos, + emptyNameSeq, emptyValSeq); + + PropertyMapper::setMappedProperties( *xShape, xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); + return xShape; +} + void PieChart::createTextLabelShape( const rtl::Reference<SvxShapeGroupAnyD>& xTextTarget, VDataSeries& rSeries, sal_Int32 nPointIndex, ShapeParam& rParam ) @@ -306,7 +407,12 @@ void PieChart::createTextLabelShape( ///get the required label placement type. Available placements are ///`AVOID_OVERLAP`, `CENTER`, `OUTSIDE` and `INSIDE`; sal_Int32 nLabelPlacement = rSeries.getLabelPlacement( - nPointIndex, m_xChartTypeModel, m_pPosHelper->isSwapXAndY()); + nPointIndex, m_xChartTypeModel, m_aPosHelper.isSwapXAndY()); + + // has an X/Y offset (relative to the OUTSIDE label default position) been provided? + const bool bHasCustomLabelPlacement = nLabelPlacement == css::chart::DataLabelPlacement::CUSTOM; + if (bHasCustomLabelPlacement) + nLabelPlacement = css::chart::DataLabelPlacement::OUTSIDE; ///when the placement is of `AVOID_OVERLAP` type a later rearrangement of ///the label position is allowed; the `createTextLabelShape` treats the @@ -314,8 +420,7 @@ void PieChart::createTextLabelShape( double nVal = rSeries.getYValue(nPointIndex); //AVOID_OVERLAP is in fact "Best fit" in the UI. - bool bMovementAllowed = nLabelPlacement == css::chart::DataLabelPlacement::AVOID_OVERLAP - || nLabelPlacement == css::chart::DataLabelPlacement::CUSTOM; + bool bMovementAllowed = nLabelPlacement == css::chart::DataLabelPlacement::AVOID_OVERLAP; if( bMovementAllowed ) nLabelPlacement = css::chart::DataLabelPlacement::CENTER; @@ -337,7 +442,7 @@ void PieChart::createTextLabelShape( ///the scene position of the label anchor point is calculated (see notes for ///`PolarLabelPositionHelper::getLabelScreenPositionAndAlignmentForUnitCircleValues`), ///and immediately transformed into the screen position. - PolarLabelPositionHelper aPolarPosHelper(m_pPosHelper.get(),m_nDimension,m_xLogicTarget); + PolarLabelPositionHelper aPolarPosHelper(&m_aPosHelper,m_nDimension,m_xLogicTarget); awt::Point aScreenPosition2D( aPolarPosHelper.getLabelScreenPositionAndAlignmentForUnitCircleValues(eAlignment, nLabelPlacement , rParam.mfUnitCircleStartAngleDegree, rParam.mfUnitCircleWidthAngleDegree @@ -346,7 +451,7 @@ void PieChart::createTextLabelShape( ///the screen position of the pie/donut center is calculated. PieLabelInfo aPieLabelInfo; aPieLabelInfo.aFirstPosition = basegfx::B2IVector( aScreenPosition2D.X, aScreenPosition2D.Y ); - awt::Point aOrigin( aPolarPosHelper.transformSceneToScreenPosition( m_pPosHelper->transformUnitCircleToScene( 0.0, 0.0, rParam.mfLogicZ+1.0 ) ) ); + awt::Point aOrigin( aPolarPosHelper.transformSceneToScreenPosition( m_aPosHelper.transformUnitCircleToScene( 0.0, 0.0, rParam.mfLogicZ+1.0 ) ) ); aPieLabelInfo.aOrigin = basegfx::B2IVector( aOrigin.X, aOrigin.Y ); ///add a scaling independent Offset if requested @@ -360,7 +465,7 @@ void PieChart::createTextLabelShape( // compute outer pie radius awt::Point aOuterCirclePoint = PlottingPositionHelper::transformSceneToScreenPosition( - m_pPosHelper->transformUnitCircleToScene( + m_aPosHelper.transformUnitCircleToScene( 0, rParam.mfUnitCircleOuterRadius, 0 ), @@ -370,29 +475,71 @@ void PieChart::createTextLabelShape( aOuterCirclePoint.Y - aPieLabelInfo.aOrigin.getY() ); double fSquaredPieRadius = aRadiusVector.scalar(aRadiusVector); double fPieRadius = sqrt( fSquaredPieRadius ); - double fAngleDegree - = rParam.mfUnitCircleStartAngleDegree + rParam.mfUnitCircleWidthAngleDegree / 2.0; - while (fAngleDegree > 360.0) - fAngleDegree -= 360.0; - while (fAngleDegree < 0.0) - fAngleDegree += 360.0; + const double fHalfWidthAngleDegree = rParam.mfUnitCircleWidthAngleDegree / 2.0; + // fAngleDegree: the angle through the center of the slice / the bisecting ray + const double fAngleDegree + = NormAngle360(rParam.mfUnitCircleStartAngleDegree + fHalfWidthAngleDegree); + // aOuterPosition: slice midpoint on the circumference, + // which is where an outside/custom label would be connected awt::Point aOuterPosition = PlottingPositionHelper::transformSceneToScreenPosition( - m_pPosHelper->transformUnitCircleToScene(fAngleDegree, rParam.mfUnitCircleOuterRadius, 0), + m_aPosHelper.transformUnitCircleToScene(fAngleDegree, rParam.mfUnitCircleOuterRadius, 0), m_xLogicTarget, m_nDimension); aPieLabelInfo.aOuterPosition = basegfx::B2IVector(aOuterPosition.X, aOuterPosition.Y); - // set the maximum text width to be used when text wrapping is enabled + /* There are basically three places where a label could be placed in a pie chart + * 1.) outside the slice + * -typically used for long labels or charts with many, thin slices + * 2.) inside the slice (center or edge) + * -typically used for charts with 5 or less slices + * 3.) in a custom location + * -typically set (by auto-positioning I presume) when labels overlap + * + * Selecting a good width for the text is critical to achieving good-looking labels. + * Our bestFit algorithm completely depends on a good starting guess. + * Lots of room for improvement here... + * Warning: complication due to 3D ovals (so can't use normal circle functions), + * donuts(m_bUseRings), auto re-scaling of the pie chart, etc. + * + * Based on observation, Microsoft uses 1/5 of the chart space as its text limit, + * although it will reduce the width (as long as it is not a custom position) + * if doing so means that the now-taller-text will fit inside the slice, + * so best if we do the same for our charts. + */ + + // set the maximum text width to be used when text wrapping is enabled (default text wrap is on) + /* A reasonable start for bestFitting a 90deg slice oriented on an Axis is 80% of the radius */ double fTextMaximumFrameWidth = 0.8 * fPieRadius; - if (nLabelPlacement == css::chart::DataLabelPlacement::OUTSIDE - && m_aAvailableOuterRect.getWidth()) + const double fCompatMaxTextLen = m_aAvailableOuterRect.getWidth() / 5.0; + if (m_aAvailableOuterRect.getWidth()) { - if ((fAngleDegree >= 67.5 && fAngleDegree <= 112.5) - || (fAngleDegree >= 247.5 && fAngleDegree <= 292.5)) - fTextMaximumFrameWidth = m_aAvailableOuterRect.getWidth() / 3.0; - else - fTextMaximumFrameWidth = 0.85 * (m_aAvailableOuterRect.getWidth() / 2.0 - fPieRadius); + if (bHasCustomLabelPlacement) + { + // if a custom width has been provided, then use that of course, + // otherwise use the interoperability-compliant 1/5 of the chart space as max width + const awt::Size aCustomSize = rSeries.getLabelCustomSize(nPointIndex); + if (aCustomSize.Width > 0) + fTextMaximumFrameWidth = aCustomSize.Width; + else + fTextMaximumFrameWidth = fCompatMaxTextLen; + } + else if (nLabelPlacement == css::chart::DataLabelPlacement::OUTSIDE) + { + // use up to 80% of the available space from the slice edge to the edge of the chart + const sal_Int32 nOuterX = aPieLabelInfo.aOuterPosition.getX(); + if (fAngleDegree < 90 || fAngleDegree > 270) // label is placed on the right side + fTextMaximumFrameWidth = 0.8 * abs(m_aAvailableOuterRect.getWidth() - nOuterX); + else // label is placed on the left side + fTextMaximumFrameWidth = 0.8 * nOuterX; + + // limited of course to the 1/5 maximum allowed for compatibility + fTextMaximumFrameWidth = std::min(fTextMaximumFrameWidth, fCompatMaxTextLen); + } } + /* TODO: better guesses for INSIDE: does the slice better handle wide text or tall/wrapped text? + * * wide: center near X-axis, shorter text content, slice > 90degree wide + * * tall: center near Y-axis, longer text content, many categories shown + */ sal_Int32 nTextMaximumFrameWidth = ceil(fTextMaximumFrameWidth); ///the text shape for the label is created @@ -402,13 +549,13 @@ void PieChart::createTextLabelShape( ///a new `PieLabelInfo` instance is initialized with all the info related to ///the current label in order to simplify later label position rearrangement; - uno::Reference< container::XChild > xChild( aPieLabelInfo.xTextShape, uno::UNO_QUERY ); + rtl::Reference< SvxShape > xChild = aPieLabelInfo.xTextShape; ///text shape could be empty; in that case there is no need to add label info if( !xChild.is() ) return; - aPieLabelInfo.xLabelGroupShape.set( xChild->getParent(), uno::UNO_QUERY ); + aPieLabelInfo.xLabelGroupShape = dynamic_cast<SvxShapeGroupAnyD*>(xChild->getParent().get()); if (bMovementAllowed && !m_bUseRings) { @@ -416,21 +563,47 @@ void PieChart::createTextLabelShape( * First off the routine try to place the label inside the related pie slice, * if this is not possible the label is placed outside. */ - if (rSeries.getLabelPlacement(nPointIndex, m_xChartTypeModel, m_pPosHelper->isSwapXAndY()) - == css::chart::DataLabelPlacement::CUSTOM - || !performLabelBestFitInnerPlacement(rParam, aPieLabelInfo)) + + /* Note: bestFit surprisingly does not adjust the width of the label, + * so having an optimal width already set when createDataLabel ran earlier + * is crucial (and currently lacking)! + * TODO: * change bestFit to treat the width as a max width, and reduce if beneficial + */ + if (!performLabelBestFitInnerPlacement(rParam, aPieLabelInfo)) { if (m_aAvailableOuterRect.getWidth()) { - if ((fAngleDegree >= 67.5 && fAngleDegree <= 112.5) - || (fAngleDegree >= 247.5 && fAngleDegree <= 292.5)) - fTextMaximumFrameWidth = m_aAvailableOuterRect.getWidth() / 3.0; - else - fTextMaximumFrameWidth - = 0.85 * (m_aAvailableOuterRect.getWidth() / 2.0 - fPieRadius); - nTextMaximumFrameWidth = ceil(fTextMaximumFrameWidth); + /* This tried to bestFit, but it didn't fit. So how best to handle this? + * + * Two possible cases relating to compatibility + * 1.) It did fit for Microsoft, but our bestFit wasn't able to do the same + * * In that case, the best response is to be as small as possible + * (the distance from the chart edge to where the label attaches to the slice) + * to avoid scaling the diagram with too long outside labels, + * and to encourage fixing the bestFit algorithm. + * 2.) It didn't fit for Microsoft either (possible, but less likely situation) + * * In that case, the compatible max length would be best + * * can expect the chart space has been properly sized to handle the max length + * + * In the native LO case, it is also best to be as small as possible, + * so that the user creating the diagram is annoyed and makes the chart area larger. + * + * Therefore, handle this by making the label as small as possible. + * + * Complication (tdf122765.pptx): it is possible for the aOuterPosition + * to be outside of the available outer rectangle (somehow), + * so in that bizarre case just try the positive value of the result... + */ + const sal_Int32 nOuterX = aPieLabelInfo.aOuterPosition.getX(); + if (fAngleDegree < 90 || fAngleDegree > 270) // label is placed on the right side + fTextMaximumFrameWidth = 0.8 * abs(m_aAvailableOuterRect.getWidth() - nOuterX); + else // label is placed on the left side + fTextMaximumFrameWidth = 0.8 * nOuterX; + + nTextMaximumFrameWidth = ceil(std::min(fTextMaximumFrameWidth, fCompatMaxTextLen)); } + // find the position to connect an Outside label to nScreenValueOffsetInRadiusDirection = (m_nDimension != 3) ? 150 : 0; aScreenPosition2D = aPolarPosHelper.getLabelScreenPositionAndAlignmentForUnitCircleValues( @@ -452,21 +625,21 @@ void PieChart::createTextLabelShape( } uno::Reference<drawing::XShapes> xShapes(xChild->getParent(), uno::UNO_QUERY); + /* question: why remove and rebuild? Can't the existing one just be changed? */ xShapes->remove(aPieLabelInfo.xTextShape); aPieLabelInfo.xTextShape = createDataLabel(xTextTarget, rSeries, nPointIndex, nVal, rParam.mfLogicYSum, aScreenPosition2D, eAlignment, 0, nTextMaximumFrameWidth); - xChild.clear(); - xChild.set(uno::Reference<container::XChild>(aPieLabelInfo.xTextShape, uno::UNO_QUERY)); + xChild = aPieLabelInfo.xTextShape; if (!xChild.is()) return; - aPieLabelInfo.xLabelGroupShape.set(xChild->getParent(), uno::UNO_QUERY); + aPieLabelInfo.xLabelGroupShape = dynamic_cast<SvxShapeGroupAnyD*>(xChild->getParent().get()); } } - bool bShowLeaderLine = rSeries.getPropertiesOfSeries() - ->getPropertyValue("ShowCustomLeaderLines") + bool bShowLeaderLine = rSeries.getModel() + ->getFastPropertyValue(PROP_DATASERIES_SHOW_CUSTOM_LEADERLINES) // "ShowCustomLeaderLines" .get<sal_Bool>(); if (m_bPieLabelsAllowToMove) { @@ -493,39 +666,30 @@ void PieChart::createTextLabelShape( { sal_Int32 nX1 = aPieLabelInfo.aOuterPosition.getX(); sal_Int32 nY1 = aPieLabelInfo.aOuterPosition.getY(); - sal_Int32 nX2 = nX1; - sal_Int32 nY2 = nY1; - if (nX1 < aRect.getMinX()) - nX2 = aRect.getMinX(); - else if (nX1 > aRect.getMaxX()) - nX2 = aRect.getMaxX(); - - if (nY1 < aRect.getMinY()) - nY2 = aRect.getMinY(); - else if (nY1 > aRect.getMaxY()) - nY2 = aRect.getMaxY(); - - sal_Int32 nSquaredDistanceFromOrigin + const sal_Int32 nX2 = std::clamp(nX1, aRect.getMinX(), aRect.getMaxX()); + const sal_Int32 nY2 = std::clamp(nY1, aRect.getMinY(), aRect.getMaxY()); + + const sal_Int32 nLabelSquaredDistanceFromOrigin = (nX2 - aOrigin.X) * (nX2 - aOrigin.X) + (nY2 - aOrigin.Y) * (nY2 - aOrigin.Y); + // can't use fSquaredPieRadius for 3D charts, since no longer a true circle + const sal_Int32 nPieEdgeSquaredDistanceFromOrigin + = (nX1 - aOrigin.X) * (nX1 - aOrigin.X) + (nY1 - aOrigin.Y) * (nY1 - aOrigin.Y); // tdf#138018 Don't show leader line when custom positioned data label is inside pie chart - if (nSquaredDistanceFromOrigin > fSquaredPieRadius) + if (nLabelSquaredDistanceFromOrigin > nPieEdgeSquaredDistanceFromOrigin) { //when the line is very short compared to the page size don't create one ::basegfx::B2DVector aLength(nX1 - nX2, nY1 - nY2); - double fPageDiagonaleLength = sqrt(double(nPageWidth) * double(nPageWidth) - + double(nPageHeight) * double(nPageHeight)); + double fPageDiagonaleLength = std::hypot(nPageWidth, nPageHeight); if ((aLength.getLength() / fPageDiagonaleLength) >= 0.01) { drawing::PointSequenceSequence aPoints{ { {nX1, nY1}, {nX2, nY2} } }; - uno::Reference<beans::XPropertySet> xProp(aPieLabelInfo.xTextShape, - uno::UNO_QUERY); VLineProperties aVLineProperties; - if (xProp.is()) + if (aPieLabelInfo.xTextShape.is()) { sal_Int32 nColor = 0; - xProp->getPropertyValue("CharColor") >>= nColor; + aPieLabelInfo.xTextShape->SvxShape::getPropertyValue("CharColor") >>= nColor; //automatic font color does not work for lines -> fallback to black if (nColor != -1) aVLineProperties.Color <<= nColor; @@ -571,19 +735,20 @@ double PieChart::getMaxOffset() return m_fMaxOffset; VDataSeries* pSeries = rSeriesList.front().get(); - uno::Reference< beans::XPropertySet > xSeriesProp( pSeries->getPropertiesOfSeries() ); - if( !xSeriesProp.is() ) + rtl::Reference< DataSeries > xSeries( pSeries->getModel() ); + if( !xSeries.is() ) return m_fMaxOffset; double fExplodePercentage=0.0; - xSeriesProp->getPropertyValue( "Offset") >>= fExplodePercentage; + xSeries->getPropertyValue( "Offset") >>= fExplodePercentage; if(fExplodePercentage>m_fMaxOffset) m_fMaxOffset=fExplodePercentage; if(!m_bSizeExcludesLabelsAndExplodedSegments) { uno::Sequence< sal_Int32 > aAttributedDataPointIndexList; - if( xSeriesProp->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList ) + // "AttributedDataPoints" + if( xSeries->getFastPropertyValue( PROP_DATASERIES_ATTRIBUTED_DATA_POINTS ) >>= aAttributedDataPointIndexList ) { for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;) { @@ -694,22 +859,14 @@ void PieChart::createShapes() ///the angle axis scale range is [0, 1]. The max_offset parameter is used ///for exploded pie chart and its value is 0.5. - ///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. - std::vector< VDataSeriesGroup >::size_type nExplodeableSlot = 0; - if( m_pPosHelper->isMathematicalOrientationRadius() && m_bUseRings ) - nExplodeableSlot = m_aZSlots.front().size()-1; - m_aLabelInfoList.clear(); m_fMaxOffset = std::numeric_limits<double>::quiet_NaN(); sal_Int32 n3DRelativeHeight = 100; - uno::Reference< beans::XPropertySet > xPropertySet( m_xChartTypeModel, uno::UNO_QUERY ); - if ( (m_nDimension==3) && xPropertySet.is()) + if ( (m_nDimension==3) && m_xChartTypeModel.is()) { try { - uno::Any aAny = xPropertySet->getPropertyValue( "3DRelativeHeight" ); + uno::Any aAny = m_xChartTypeModel->getFastPropertyValue( PROP_PIECHARTTYPE_3DRELATIVEHEIGHT ); // "3DRelativeHeight" aAny >>= n3DRelativeHeight; } catch (const uno::Exception&) { } @@ -720,8 +877,6 @@ void PieChart::createShapes() ///(m_bUseRings||fSlotX<0.5) for( double fSlotX=0; aXSlotIter != aXSlotEnd && (m_bUseRings||fSlotX<0.5 ); ++aXSlotIter, fSlotX+=1.0 ) { - ShapeParam aParam; - std::vector< std::unique_ptr<VDataSeries> >* pSeriesList = &(aXSlotIter->m_aSeriesVector); if(pSeriesList->empty())//there should be only one series in each x slot continue; @@ -729,17 +884,17 @@ void PieChart::createShapes() if(!pSeries) continue; - bool bHasFillColorMapping = pSeries->hasPropertyMapping("FillColor"); - /// The angle degree offset is set by the same property of the /// data series. /// Counter-clockwise offset from the 3 o'clock position. - m_pPosHelper->m_fAngleDegreeOffset = pSeries->getStartingAngle(); + m_aPosHelper.m_fAngleDegreeOffset = pSeries->getStartingAngle(); ///iterate through all points to get the sum of all entries of ///the current data series sal_Int32 nPointIndex=0; sal_Int32 nPointCount=pSeries->getTotalPointCount(); + ShapeParam aParam; + for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ ) { double fY = pSeries->getYValue( nPointIndex ); @@ -752,134 +907,457 @@ 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; + } - double fLogicYForNextPoint = 0.0; - ///iterate through all points to create shapes - for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ ) + 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 (eSubType) { + case PieChartSubType_NONE: + pDataSrc = &normalPieSrc; + createOneRing(SubPieType::NONE, fSlotX, aParam, xSeriesTarget, + xTextTarget, pSeries, pDataSrc, n3DRelativeHeight); + break; + case PieChartSubType_BAR: { - double fLogicInnerRadius, fLogicOuterRadius; - - ///compute the maximum relative distance offset of the current slice - ///from the pie center - ///it is worth noting that after the first invocation the maximum - ///offset value is cached, so it is evaluated only once per each - ///call to `createShapes` - double fOffset = getMaxOffset(); - - ///compute the outer and the inner radius for the current ring slice - bool bIsVisible = m_pPosHelper->getInnerAndOuterRadius( fSlotX+1.0, fLogicInnerRadius, fLogicOuterRadius, m_bUseRings, fOffset ); - if( !bIsVisible ) - continue; + pDataSrc = &ofPieSrc; + createOneRing(SubPieType::LEFT, 0, aParam, xSeriesTarget, + xTextTarget, pSeries, pDataSrc, n3DRelativeHeight); + createOneBar(SubPieType::RIGHT, aParam, xSeriesTarget, + xTextTarget, pSeries, pDataSrc, n3DRelativeHeight); + + // + // Draw connecting lines + // + double xl0, xl1, yl0, yl1, x0, y0, x1, y1, y2, y3; + + // Get coordinates of "corners" of left composite wedge + sal_Int32 nEnd = pDataSrc->getNPoints(pSeries, SubPieType::LEFT); + double compFrac = pDataSrc->getData(pSeries, nEnd - 1, + SubPieType::LEFT) / aParam.mfLogicYSum; + if (compFrac < 0.5) { + xl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale * + cos(compFrac * M_PI) + m_fLeftShift; + yl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale * + sin(compFrac * M_PI); + } else { + xl0 = m_fLeftShift; + yl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale; + } - aParam.mfDepth = getTransformedDepth() * (n3DRelativeHeight / 100.0); + // Coordinates of bar top left corner + xl1 = m_fBarLeft; + yl1 = m_fFullBarHeight / 2; - rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries, xSeriesTarget); - ///collect data point information (logic coordinates, style ): - double fLogicYValue = fabs(pSeries->getYValue( nPointIndex )); - if( std::isnan(fLogicYValue) ) - continue; - if(fLogicYValue==0.0)//@todo: continue also if the resolution is too small - continue; - double fLogicYPos = fLogicYForNextPoint; - fLogicYForNextPoint += fLogicYValue; + x0 = m_aPosHelper.transformUnitCircleToScene(0, xl0, 0).PositionX; + y0 = m_aPosHelper.transformUnitCircleToScene(90, yl0, 0).PositionY; + x1 = m_aPosHelper.transformUnitCircleToScene(0, xl1, 0).PositionX; + y1 = m_aPosHelper.transformUnitCircleToScene(90, yl1, 0).PositionY; + y2 = m_aPosHelper.transformUnitCircleToScene(90, -yl0, 0).PositionY; + y3 = m_aPosHelper.transformUnitCircleToScene(90, -yl1, 0).PositionY; + + std::vector<std::vector<css::drawing::Position3D>> linePts; + linePts.resize(2); + linePts[0].push_back(css::drawing::Position3D(x0, y0, aParam.mfLogicZ)); + linePts[0].push_back(css::drawing::Position3D(x1, y1, aParam.mfLogicZ)); + linePts[1].push_back(css::drawing::Position3D(x0, y2, aParam.mfLogicZ)); + linePts[1].push_back(css::drawing::Position3D(x1, y3, aParam.mfLogicZ)); + + VLineProperties aVLineProperties; // default black + + //create line + rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = + getSeriesGroupShape(pSeries, xSeriesTarget); + rtl::Reference<SvxShape> xShape = ShapeFactory::createLine2D( + xSeriesGroupShape_Shapes, linePts, &aVLineProperties); + + // need to set properties? + //PropertyMapper::setMappedProperties( *xShape, xObjectProperties, + // PropertyMapper::getPropertyNameMapForLineSeriesProperties() ); + + break; + } + case PieChartSubType_PIE: + { + pDataSrc = &ofPieSrc; + createOneRing(SubPieType::LEFT, 0, aParam, xSeriesTarget, + xTextTarget, pSeries, pDataSrc, n3DRelativeHeight); + createOneRing(SubPieType::RIGHT, 0, aParam, xSeriesTarget, + xTextTarget, pSeries, pDataSrc, n3DRelativeHeight); + + // + // Draw connecting lines + // + double xl0, xl1, yl0, yl1, x0, y0, x1, y1, y2, y3; + + // Get coordinates of "corners" of left composite wedge + sal_Int32 nEnd = pDataSrc->getNPoints(pSeries, SubPieType::LEFT); + double compFrac = pDataSrc->getData(pSeries, nEnd - 1, + SubPieType::LEFT) / aParam.mfLogicYSum; + // The following isn't quite right. The tangent points on the left + // pie are only at pi/2 and -pi/2 for composite wedges over 1/2 the + // total if left and right pies are the same diameter. And the + // threshold of 1/2 isn't quite right either. So there + // really should be a more sophisticated approach here. TODO + if (compFrac < 0.5) { + // Translated, per below + xl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale * + cos(compFrac * M_PI) + m_fLeftShift - m_fRightShift; + yl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale * + sin(compFrac * M_PI); + } else { + // Translated, per below + xl0 = m_fLeftShift - m_fRightShift; + yl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale; + } + + // Compute tangent point on the right-hand circle of the line + // through (xl0, yl0). If we translate things so the right-hand + // circle is centered on the origin, then this point (x,y) + // satisfies these two equations, where r is the radius of the + // right-hand circle: + // (1) x^2 + y^2 = r^2 + // (2) (y - yl0) / (x - xl0) = -x / y + const double r = aParam.mfUnitCircleOuterRadius * m_fRightScale; + + xl1 = (r*r * xl0 + yl0 * r * sqrt(xl0*xl0 + yl0*yl0 - r*r)) / + (xl0*xl0 + yl0*yl0); + yl1 = sqrt(r*r - xl1*xl1); + + // Now translate back to the coordinates we use + xl0 += m_fRightShift; + xl1 += m_fRightShift; + + x0 = m_aPosHelper.transformUnitCircleToScene(0, xl0, 0).PositionX; + y0 = m_aPosHelper.transformUnitCircleToScene(90, yl0, 0).PositionY; + x1 = m_aPosHelper.transformUnitCircleToScene(0, xl1, 0).PositionX; + y1 = m_aPosHelper.transformUnitCircleToScene(90, yl1, 0).PositionY; + y2 = m_aPosHelper.transformUnitCircleToScene(90, -yl0, 0).PositionY; + y3 = m_aPosHelper.transformUnitCircleToScene(90, -yl1, 0).PositionY; + + std::vector<std::vector<css::drawing::Position3D>> linePts; + linePts.resize(2); + linePts[0].push_back(css::drawing::Position3D(x0, y0, aParam.mfLogicZ)); + linePts[0].push_back(css::drawing::Position3D(x1, y1, aParam.mfLogicZ)); + linePts[1].push_back(css::drawing::Position3D(x0, y2, aParam.mfLogicZ)); + linePts[1].push_back(css::drawing::Position3D(x1, y3, aParam.mfLogicZ)); + + VLineProperties aVLineProperties; // default black + + //create line + rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = + getSeriesGroupShape(pSeries, xSeriesTarget); + rtl::Reference<SvxShape> xShape = ShapeFactory::createLine2D( + xSeriesGroupShape_Shapes, linePts, &aVLineProperties); + + break; + } + default: + assert(false); // this shouldn't happen + } + }//next x slot +} + +static sal_Int32 propIndex( + sal_Int32 nPointIndex, + enum SubPieType eType, + const PieDataSrcBase *pDataSrc, + VDataSeries* pSeries) +{ + + switch (eType) { + case SubPieType::LEFT: + if (nPointIndex == pDataSrc->getNPoints(pSeries, + SubPieType::LEFT) - 1) { + return pSeries->getTotalPointCount(); + } else { + return nPointIndex; + } + break; + case SubPieType::RIGHT: + return pDataSrc->getNPoints(pSeries, SubPieType::LEFT) + + nPointIndex - 1; + break; + case SubPieType::NONE: + return nPointIndex; + break; + default: // shouldn't happen + assert(false); + return 0; // suppress compile warning + } +} + + +void PieChart::createOneRing( + enum SubPieType eType, + double fSlotX, + ShapeParam& aParam, + const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, + const rtl::Reference<SvxShapeGroup>& xTextTarget, + VDataSeries* pSeries, + const PieDataSrcBase *pDataSrc, + sal_Int32 n3DRelativeHeight) +{ + bool bHasFillColorMapping = pSeries->hasPropertyMapping("FillColor"); + + 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; + } + + // determine the starting angle around the ring + auto sAngle = [&]() + { + if (eType == SubPieType::LEFT) { + // Left of-pie has the "composite" wedge (the one expanded in the right + // subgraph) facing to the right in the chart, to allow the expansion + // lines to meet it + const double compositeVal = pDataSrc->getData(pSeries, nRingPtCnt - 1, eType); + const double degAng = compositeVal * 360 / (ringSum * 2); + return m_aPosHelper.clockwiseWedges() ? 360 - degAng : degAng; + } else { + /// The angle degree offset is set by the same property of the + /// data series. + /// Counter-clockwise offset from the 3 o'clock position. + return static_cast<double>(pSeries->getStartingAngle()); + } + }; + + m_aPosHelper.m_fAngleDegreeOffset = sAngle(); + + ///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. + std::vector< VDataSeriesGroup >::size_type nExplodeableSlot = 0; + if( m_aPosHelper.isMathematicalOrientationRadius() && m_bUseRings ) + nExplodeableSlot = m_aZSlots.front().size()-1; + + double fLogicYForNextPoint = 0.0; + ///iterate through all points to create shapes + for(sal_Int32 nPointIndex = 0; nPointIndex < nRingPtCnt; nPointIndex++ ) + { + double fLogicInnerRadius, fLogicOuterRadius; + + ///compute the maximum relative distance offset of the current slice + ///from the pie center + ///it is worth noting that after the first invocation the maximum + ///offset value is cached, so it is evaluated only once per each + ///call to `createShapes` + double fOffset = getMaxOffset(); + + ///compute the outer and the inner radius for the current ring slice + bool bIsVisible = m_aPosHelper.getInnerAndOuterRadius( fSlotX+1.0, fLogicInnerRadius, fLogicOuterRadius, m_bUseRings, fOffset ); + if( !bIsVisible ) + continue; + + aParam.mfDepth = getTransformedDepth() * (n3DRelativeHeight / 100.0); + + rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries, xSeriesTarget); + + ///collect data point information (logic coordinates, style ): + 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 + continue; + 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 + //iterate through all subsystems to create partial points + { + //logic values on angle axis: + 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 + ///belonging to the outer ring + aParam.mfExplodePercentage = 0.0; + bool bDoExplode = ( nExplodeableSlot == static_cast< std::vector< VDataSeriesGroup >::size_type >(fSlotX) ); + if(bDoExplode) try { - //logic values on angle axis: - double fLogicStartAngleValue = fLogicYPos / aParam.mfLogicYSum; - double fLogicEndAngleValue = (fLogicYPos+fLogicYValue) / aParam.mfLogicYSum; - - ///note that the explode percentage is set to the `Offset` - ///property of the current data series entry only for slices - ///belonging to the outer ring - aParam.mfExplodePercentage = 0.0; - bool bDoExplode = ( nExplodeableSlot == static_cast< std::vector< VDataSeriesGroup >::size_type >(fSlotX) ); - if(bDoExplode) try - { - xPointProperties->getPropertyValue( "Offset") >>= aParam.mfExplodePercentage; - } - catch( const uno::Exception& ) - { - TOOLS_WARN_EXCEPTION("chart2", "" ); - } + xPointProperties->getPropertyValue( "Offset") >>= aParam.mfExplodePercentage; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } - ///see notes for `PolarPlottingPositionHelper` methods - ///transform to unit circle: - aParam.mfUnitCircleWidthAngleDegree = m_pPosHelper->getWidthAngleDegree( fLogicStartAngleValue, fLogicEndAngleValue ); - aParam.mfUnitCircleStartAngleDegree = m_pPosHelper->transformToAngleDegree( fLogicStartAngleValue ); - aParam.mfUnitCircleInnerRadius = m_pPosHelper->transformToRadius( fLogicInnerRadius ); - aParam.mfUnitCircleOuterRadius = m_pPosHelper->transformToRadius( fLogicOuterRadius ); - - ///create data point - aParam.mfLogicZ = -1.0; // For 3D pie chart label position - rtl::Reference<SvxShape> xPointShape = - createDataPoint( - xSeriesGroupShape_Shapes, xPointProperties, aParam); - - ///point color: - if (!pSeries->hasPointOwnColor(nPointIndex) && m_xColorScheme.is()) - { - xPointShape->setPropertyValue("FillColor", - uno::Any(m_xColorScheme->getColorByIndex( nPointIndex ))); - } + ///see notes for `PolarPlottingPositionHelper` methods + ///transform to unit circle: + aParam.mfUnitCircleWidthAngleDegree = m_aPosHelper.getWidthAngleDegree( fLogicStartAngleValue, fLogicEndAngleValue ); + aParam.mfUnitCircleStartAngleDegree = m_aPosHelper.transformToAngleDegree( fLogicStartAngleValue ); + aParam.mfUnitCircleInnerRadius = m_aPosHelper.transformToRadius( fLogicInnerRadius ); + aParam.mfUnitCircleOuterRadius = m_aPosHelper.transformToRadius( fLogicOuterRadius ); + + ///create data point + aParam.mfLogicZ = -1.0; // For 3D pie chart label position + + // 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, nRingPtCnt, + bConcentricExplosion); + + // Handle coloring of the composite wedge + sal_Int32 nPropIdx = propIndex(nPointIndex, eType, pDataSrc, + pSeries); + + ///point color: + if (!pSeries->hasPointOwnColor(nPropIdx) && m_xColorScheme.is()) + { + xPointShape->setPropertyValue("FillColor", + uno::Any(m_xColorScheme->getColorByIndex( nPropIdx ))); + } - if(bHasFillColorMapping) + if(bHasFillColorMapping) + { + double nPropVal = pSeries->getValueByProperty(nPropIdx, "FillColor"); + if(!std::isnan(nPropVal)) { - double nPropVal = pSeries->getValueByProperty(nPointIndex, "FillColor"); - if(!std::isnan(nPropVal)) - { - xPointShape->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>( nPropVal))); - } + xPointShape->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>( nPropVal))); } + } - ///create label - createTextLabelShape(xTextTarget, *pSeries, nPointIndex, aParam); + ///create label + createTextLabelShape(xTextTarget, *pSeries, nPropIdx, aParam); - if(!bDoExplode) - { - ShapeFactory::setShapeName( xPointShape - , ObjectIdentifier::createPointCID( pSeries->getPointCID_Stub(), nPointIndex ) ); - } - else try - { - ///enable dragging of outer segments - - double fAngle = aParam.mfUnitCircleStartAngleDegree + aParam.mfUnitCircleWidthAngleDegree/2.0; - double fMaxDeltaRadius = aParam.mfUnitCircleOuterRadius-aParam.mfUnitCircleInnerRadius; - drawing::Position3D aOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius, aParam.mfLogicZ ); - drawing::Position3D aNewOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius + fMaxDeltaRadius, aParam.mfLogicZ ); - - sal_Int32 nOffsetPercent( static_cast<sal_Int32>(aParam.mfExplodePercentage * 100.0) ); - - awt::Point aMinimumPosition( PlottingPositionHelper::transformSceneToScreenPosition( - aOrigin, m_xLogicTarget, m_nDimension ) ); - awt::Point aMaximumPosition( PlottingPositionHelper::transformSceneToScreenPosition( - aNewOrigin, m_xLogicTarget, m_nDimension ) ); - - //enable dragging of piesegments - OUString aPointCIDStub( ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT - , pSeries->getSeriesParticle() - , ObjectIdentifier::getPieSegmentDragMethodServiceName() - , ObjectIdentifier::createPieSegmentDragParameterString( - nOffsetPercent, aMinimumPosition, aMaximumPosition ) - ) ); - - ShapeFactory::setShapeName( xPointShape - , ObjectIdentifier::createPointCID( aPointCIDStub, nPointIndex ) ); - } - catch( const uno::Exception& ) - { - TOOLS_WARN_EXCEPTION("chart2", "" ); - } - }//next series in x slot (next y slot) - }//next category - }//next x slot + if(!bDoExplode) + { + ShapeFactory::setShapeName( xPointShape + , ObjectIdentifier::createPointCID( + pSeries->getPointCID_Stub(), nPropIdx ) ); + } + else try + { + ///enable dragging of outer segments + + double fAngle = aParam.mfUnitCircleStartAngleDegree + aParam.mfUnitCircleWidthAngleDegree/2.0; + double fMaxDeltaRadius = aParam.mfUnitCircleOuterRadius-aParam.mfUnitCircleInnerRadius; + drawing::Position3D aOrigin = m_aPosHelper.transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius, aParam.mfLogicZ ); + drawing::Position3D aNewOrigin = m_aPosHelper.transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius + fMaxDeltaRadius, aParam.mfLogicZ ); + + sal_Int32 nOffsetPercent( static_cast<sal_Int32>(aParam.mfExplodePercentage * 100.0) ); + + awt::Point aMinimumPosition( PlottingPositionHelper::transformSceneToScreenPosition( + aOrigin, m_xLogicTarget, m_nDimension ) ); + awt::Point aMaximumPosition( PlottingPositionHelper::transformSceneToScreenPosition( + aNewOrigin, m_xLogicTarget, m_nDimension ) ); + + //enable dragging of piesegments + OUString aPointCIDStub( ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT + , pSeries->getSeriesParticle() + , ObjectIdentifier::getPieSegmentDragMethodServiceName() + , ObjectIdentifier::createPieSegmentDragParameterString( + nOffsetPercent, aMinimumPosition, aMaximumPosition ) + ) ); + + ShapeFactory::setShapeName( xPointShape + , ObjectIdentifier::createPointCID( aPointCIDStub, + nPropIdx ) ); + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } + }//next series in x slot (next y slot) + }//next category +} + +void PieChart::createOneBar( + enum SubPieType eType, + ShapeParam& aParam, + const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, + const rtl::Reference<SvxShapeGroup>& xTextTarget, + VDataSeries* pSeries, + const PieDataSrcBase *pDataSrc, + sal_Int32 n3DRelativeHeight) +{ + bool bHasFillColorMapping = pSeries->hasPropertyMapping("FillColor"); + + sal_Int32 nBarPtCnt = pDataSrc->getNPoints(pSeries, eType); + + // Find sum of entries for this bar chart + double barSum = 0; + for (sal_Int32 nPointIndex = 0; nPointIndex < nBarPtCnt; nPointIndex++ ) { + double fY = pDataSrc->getData(pSeries, nPointIndex, eType); + if (!std::isnan(fY) ) barSum += fY; + } + + double fBarBottom = 0.0; + double fBarTop = -0.5; // make the bar go from -0.5 to 0.5 + ///iterate through all points to create shapes + for(sal_Int32 nPointIndex = 0; nPointIndex < nBarPtCnt; nPointIndex++ ) + { + aParam.mfDepth = getTransformedDepth() * (n3DRelativeHeight / 100.0); + + rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries, xSeriesTarget); + + ///collect data point information (logic coordinates, style ): + double fY = pDataSrc->getData(pSeries, nPointIndex, eType) / barSum; + if( std::isnan(fY) ) + continue; + if(fY==0.0)//@todo: continue also if the resolution is too small + continue; + fBarBottom = fBarTop; + fBarTop += fY; + + uno::Reference< beans::XPropertySet > xPointProperties = + pDataSrc->getProps(pSeries, nPointIndex, eType); + + ///create data point + aParam.mfLogicZ = -1.0; // For 3D pie chart label position + + rtl::Reference<SvxShape> xPointShape = + createBarDataPoint(xSeriesGroupShape_Shapes, + xPointProperties, aParam, + fBarBottom, fBarTop); + + sal_Int32 nPropIdx = propIndex(nPointIndex, eType, pDataSrc, pSeries); + + ///point color: + if (!pSeries->hasPointOwnColor(nPropIdx) && m_xColorScheme.is()) + { + xPointShape->setPropertyValue("FillColor", + uno::Any(m_xColorScheme->getColorByIndex( nPropIdx ))); + } + + + if(bHasFillColorMapping) + { + double nPropVal = pSeries->getValueByProperty(nPropIdx, "FillColor"); + if(!std::isnan(nPropVal)) + { + xPointShape->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>( nPropVal))); + } + } + + ///create label + createTextLabelShape(xTextTarget, *pSeries, nPropIdx, aParam); + + ShapeFactory::setShapeName( xPointShape, + ObjectIdentifier::createPointCID( pSeries->getPointCID_Stub(), + nPropIdx ) ); + }//next category } PieChart::PieLabelInfo::PieLabelInfo() @@ -1129,7 +1607,7 @@ bool PieChart::tryMoveLabels( PieLabelInfo const * pFirstBorder, PieLabelInfo co PieLabelInfo* p2 = pCenter->pNext; //return true when successful - bool bLabelOrderIsAntiClockWise = m_pPosHelper->isMathematicalOrientationAngle(); + bool bLabelOrderIsAntiClockWise = m_aPosHelper.isMathematicalOrientationAngle(); ///two loops are performed simultaneously: the outer loop iterates on ///`PieLabelInfo` objects in the list starting from the central element @@ -1239,7 +1717,7 @@ void PieChart::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& rPageSi if(!bMoveableFound) return; - double fPageDiagonaleLength = sqrt( double(rPageSize.Width)*double(rPageSize.Width) + double(rPageSize.Height)*double(rPageSize.Height) ); + double fPageDiagonaleLength = std::hypot(rPageSize.Width, rPageSize.Height); if( fPageDiagonaleLength == 0.0 ) return; @@ -1269,20 +1747,11 @@ void PieChart::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& rPageSi { if( labelInfo.bMoved && labelInfo.bShowLeaderLine ) { + const basegfx::B2IRectangle aRect(lcl_getRect(labelInfo.xLabelGroupShape)); sal_Int32 nX1 = labelInfo.aOuterPosition.getX(); sal_Int32 nY1 = labelInfo.aOuterPosition.getY(); - sal_Int32 nX2 = nX1; - sal_Int32 nY2 = nY1; - ::basegfx::B2IRectangle aRect( lcl_getRect( labelInfo.xLabelGroupShape ) ); - if( nX1 < aRect.getMinX() ) - nX2 = aRect.getMinX(); - else if( nX1 > aRect.getMaxX() ) - nX2 = aRect.getMaxX(); - - if( nY1 < aRect.getMinY() ) - nY2 = aRect.getMinY(); - else if( nY1 > aRect.getMaxY() ) - nY2 = aRect.getMaxY(); + const sal_Int32 nX2 = std::clamp(nX1, aRect.getMinX(), aRect.getMaxX()); + const sal_Int32 nY2 = std::clamp(nY1, aRect.getMinY(), aRect.getMaxY()); //when the line is very short compared to the page size don't create one ::basegfx::B2DVector aLength(nX1-nX2, nY1-nY2); @@ -1291,11 +1760,10 @@ void PieChart::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& rPageSi drawing::PointSequenceSequence aPoints{ { {nX1, nY1}, {nX2, nY2} } }; - uno::Reference< beans::XPropertySet > xProp( labelInfo.xTextShape, uno::UNO_QUERY); - if( xProp.is() ) + if( labelInfo.xTextShape.is() ) { sal_Int32 nColor = 0; - xProp->getPropertyValue("CharColor") >>= nColor; + labelInfo.xTextShape->SvxShape::getPropertyValue("CharColor") >>= nColor; if( nColor != -1 )//automatic font color does not work for lines -> fallback to black aVLineProperties.Color <<= nColor; } @@ -1394,7 +1862,7 @@ bool PieChart::performLabelBestFitInnerPlacement(ShapeParam& rShapeParam, PieLab // get the middle point of the arc representing the pie slice border double fLogicZ = rShapeParam.mfLogicZ + 1.0; awt::Point aMiddleArcPoint = PlottingPositionHelper::transformSceneToScreenPosition( - m_pPosHelper->transformUnitCircleToScene( + m_aPosHelper.transformUnitCircleToScene( fBisectingRayAngleDeg, rShapeParam.mfUnitCircleOuterRadius, fLogicZ ), @@ -1480,8 +1948,7 @@ bool PieChart::performLabelBestFitInnerPlacement(ShapeParam& rShapeParam, PieLab // compute the length of the diagonal vector d, // that is the distance between P and F - double fSquaredDistancePF = fDistancePM * fDistancePM + fOrthogonalEdgeLength * fOrthogonalEdgeLength; - double fDistancePF = sqrt( fSquaredDistancePF ); + double fDistancePF = std::hypot(fDistancePM, fOrthogonalEdgeLength); SAL_INFO( "chart2.pie.label.bestfit.inside", " width = " << fLabelWidth ); @@ -1691,6 +2158,92 @@ bool PieChart::performLabelBestFitInnerPlacement(ShapeParam& rShapeParam, PieLab return true; } +//======================= +// class PieDataSrc +//======================= +double PieDataSrc::getData(const VDataSeries* pSeries, sal_Int32 nPtIdx, + [[maybe_unused]] 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 nPts = pSeries->getTotalPointCount(); + const sal_Int32 n = nPts - 3; + if (eType == SubPieType::LEFT) { + // nPtIdx should be in [0, n] + if (nPtIdx < n) { + return pSeries->getPropertiesOfPoint( nPtIdx ); + } else { + // The aggregated wedge + assert(nPtIdx == n); + return pSeries->getPropertiesOfPoint(nPts); + } + } 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 5bd25eed53e6..ccbe9cb94d94 100644 --- a/chart2/source/view/charttypes/PieChart.hxx +++ b/chart2/source/view/charttypes/PieChart.hxx @@ -21,13 +21,103 @@ #include <memory> #include <VSeriesPlotter.hxx> +#include <PlottingPositionHelper.hxx> #include <basegfx/vector/b2ivector.hxx> #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 { -class PiePositionHelper; +class PiePositionHelper : public PolarPlottingPositionHelper +{ +public: + PiePositionHelper( double fAngleDegreeOffset ); + + bool getInnerAndOuterRadius( double fCategoryX, double& fLogicInnerRadius, double& fLogicOuterRadius, bool bUseRings, double fMaxOffset ) const; + + // Determine if the pie wedges are ordered clockwise (returns true) or + // counterclockwise (returns false) + bool clockwiseWedges() const; + +public: + //Distance between different category rings, seen relative to width of a ring: + double m_fRingDistance; //>=0 m_fRingDistance=1 --> distance == width +}; + +enum class SubPieType { + NONE, // solo pie or donut + LEFT, // left pie in pie-of-pie + RIGHT // right pie in pie-of-pie +}; + + +//======================= +// class PieDataSrcBase +//======================= +class PieDataSrcBase +{ +public: + PieDataSrcBase() = default; + virtual ~PieDataSrcBase() = default; + + // Number of data points for 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 +{ +public: + sal_Int32 getNPoints(const VDataSeries* pSeries, + enum SubPieType eType) const; + + double getData(const VDataSeries* pSeries, sal_Int32 nPtIdx, + [[maybe_unused]]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 constexpr sal_Int32 minPoints = 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; @@ -35,7 +125,7 @@ class PieChart : public VSeriesPlotter public: PieChart() = delete; - PieChart( const css::uno::Reference< css::chart2::XChartType >& xChartTypeModel + PieChart( const rtl::Reference< ::chart::ChartType >& xChartTypeModel , sal_Int32 nDimensionCount, bool bExcludingPositioning ); virtual ~PieChart() override; @@ -65,10 +155,18 @@ public: private: //methods rtl::Reference<SvxShape> createDataPoint( + enum SubPieType eType, const rtl::Reference<SvxShapeGroupAnyD>& xTarget, const css::uno::Reference<css::beans::XPropertySet>& xObjectProperties, - const ShapeParam& rParam ); + const ShapeParam& rParam, + const sal_Int32 nPointCount, + const bool bConcentricExplosion); + rtl::Reference<SvxShape> createBarDataPoint( + const rtl::Reference<SvxShapeGroupAnyD>& xTarget, + const uno::Reference<beans::XPropertySet>& xObjectProperties, + const ShapeParam& rParam, + double fBarSegBottom, double fBarSegTop); /** This method creates a text shape for a label of a data point. * * @param xTextTarget @@ -107,11 +205,48 @@ struct PieLabelInfo; bool performLabelBestFitInnerPlacement( ShapeParam& rShapeParam , PieLabelInfo const & rPieLabelInfo ); + // A standalone pie, one pie in a pie-of-pie, or one ring of a donut + void createOneRing([[maybe_unused]]enum SubPieType eType + , double fSlotX + , ShapeParam& aParam + , const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget + , const rtl::Reference<SvxShapeGroup>& xTextTarget + , VDataSeries* pSeries + , const PieDataSrcBase *pDataSrc + , sal_Int32 n3DRelativeHeight); + + // A bar chart in a bar-of-pie + void createOneBar( + enum SubPieType eType, + ShapeParam& aParam, + const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget, + const rtl::Reference<SvxShapeGroup>& xTextTarget, + VDataSeries* pSeries, + const PieDataSrcBase *pDataSrc, + sal_Int32 n3DRelativeHeight); + private: //member - std::unique_ptr<PiePositionHelper> - m_pPosHelper; + // Constants for of-pie charts. Some of these will want to become + // user-selectable values. TODO + + // Radius scalings for left and right of-pie subcharts + static constexpr double m_fLeftScale = 2.0/3; + static constexpr double m_fRightScale = 1.0/3; + // Shifts left/right for of-pie subcharts + static constexpr double m_fLeftShift = -0.75; + static constexpr double m_fRightShift = 0.75; + // Height of bar-of-pie bar + static constexpr double m_fFullBarHeight = 1.0; + // Bar-of-pie bar left side position + static constexpr double m_fBarLeft = 0.75; + // Bar-of-pie bar right side position + static constexpr double m_fBarRight = 1.25; + + PiePositionHelper m_aPosHelper; + bool m_bUseRings; bool m_bSizeExcludesLabelsAndExplodedSegments; + ::css::chart2::PieChartSubType m_eSubType; struct PieLabelInfo { @@ -119,8 +254,8 @@ private: //member bool moveAwayFrom( const PieLabelInfo* pFix, const css::awt::Size& rPageSize , bool bMoveHalfWay, bool bMoveClockwise ); - css::uno::Reference< css::drawing::XShape > xTextShape; - css::uno::Reference< css::drawing::XShape > xLabelGroupShape; + rtl::Reference< SvxShapeText > xTextShape; + rtl::Reference< SvxShapeGroupAnyD > xLabelGroupShape; ::basegfx::B2IVector aFirstPosition; ::basegfx::B2IVector aOuterPosition; ::basegfx::B2IVector aOrigin; @@ -138,6 +273,7 @@ private: //member double m_fMaxOffset; /// cached max offset value (init'ed to NaN) }; + } //namespace chart /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/Splines.cxx b/chart2/source/view/charttypes/Splines.cxx index bd54b266345a..15980b638bc8 100644 --- a/chart2/source/view/charttypes/Splines.cxx +++ b/chart2/source/view/charttypes/Splines.cxx @@ -19,12 +19,11 @@ #include "Splines.hxx" #include <osl/diagnose.h> -#include <com/sun/star/drawing/PolyPolygonShape3D.hpp> #include <com/sun/star/drawing/Position3D.hpp> #include <vector> #include <algorithm> -#include <memory> +#include <optional> #include <cmath> #include <limits> @@ -121,7 +120,7 @@ lcl_SplineCalculation::lcl_SplineCalculation( m_fYp1( fY1FirstDerivation ), m_fYpN( fYnFirstDerivation ), m_nKLow( 0 ), - m_nKHigh( rSortedPoints.size() - 1 ), + m_nKHigh( m_aPoints.size() - 1 ), m_fLastInterpolatedValue(std::numeric_limits<double>::infinity()) { Calculate(); @@ -133,7 +132,7 @@ lcl_SplineCalculation::lcl_SplineCalculation( m_fYp1( 0.0 ), /*dummy*/ m_fYpN( 0.0 ), /*dummy*/ m_nKLow( 0 ), - m_nKHigh( rSortedPoints.size() - 1 ), + m_nKHigh( m_aPoints.size() - 1 ), m_fLastInterpolatedValue(std::numeric_limits<double>::infinity()) { CalculatePeriodic(); @@ -411,26 +410,19 @@ bool createParameterT(const tPointVecType& rUniquePoints, double* t) bool bIsSuccessful = true; const lcl_tSizeType n = rUniquePoints.size() - 1; t[0]=0.0; - double dx = 0.0; - double dy = 0.0; - double fDiffMax = 1.0; //dummy values double fDenominator = 0.0; // initialized for summing up for (lcl_tSizeType i=1; i<=n ; ++i) { // 4th root(dx^2+dy^2) - dx = rUniquePoints[i].first - rUniquePoints[i-1].first; - dy = rUniquePoints[i].second - rUniquePoints[i-1].second; - // scaling to avoid underflow or overflow - fDiffMax = std::max(fabs(dx), fabs(dy)); - if (fDiffMax == 0.0) + double dx = rUniquePoints[i].first - rUniquePoints[i-1].first; + double dy = rUniquePoints[i].second - rUniquePoints[i-1].second; + if (dx == 0 && dy == 0) { bIsSuccessful = false; break; } else { - dx /= fDiffMax; - dy /= fDiffMax; - fDenominator += sqrt(sqrt(dx * dx + dy * dy)) * sqrt(fDiffMax); + fDenominator += sqrt(std::hypot(dx, dy)); } } if (fDenominator == 0.0) @@ -444,13 +436,9 @@ bool createParameterT(const tPointVecType& rUniquePoints, double* t) double fNumerator = 0.0; for (lcl_tSizeType i=1; i<=j ; ++i) { - dx = rUniquePoints[i].first - rUniquePoints[i-1].first; - dy = rUniquePoints[i].second - rUniquePoints[i-1].second; - fDiffMax = std::max(fabs(dx), fabs(dy)); - // same as above, so should not be zero - dx /= fDiffMax; - dy /= fDiffMax; - fNumerator += sqrt(sqrt(dx * dx + dy * dy)) * sqrt(fDiffMax); + double dx = rUniquePoints[i].first - rUniquePoints[i-1].first; + double dy = rUniquePoints[i].second - rUniquePoints[i-1].second; + fNumerator += sqrt(std::hypot(dx, dy)); } t[j] = fNumerator / fDenominator; @@ -581,8 +569,8 @@ void SplineCalculater::CalculateCubicSplines( // generate a spline for each coordinate. It holds the complete // information to calculate each point of the curve - std::unique_ptr<lcl_SplineCalculation> aSplineX; - std::unique_ptr<lcl_SplineCalculation> aSplineY; + std::optional<lcl_SplineCalculation> aSplineX; + std::optional<lcl_SplineCalculation> aSplineY; // lcl_SplineCalculation* aSplineZ; the z-coordinates of all points in // a data series are equal. No spline calculation needed, but copy // coordinate to output @@ -592,16 +580,15 @@ void SplineCalculater::CalculateCubicSplines( pOld[ 0 ].PositionZ == pOld[nMaxIndexPoints].PositionZ && nMaxIndexPoints >=2 ) { // periodic spline - aSplineX.reset(new lcl_SplineCalculation( std::move(aInputX))); - aSplineY.reset(new lcl_SplineCalculation( std::move(aInputY))); - // aSplineZ = new lcl_SplineCalculation( aInputZ) ; + aSplineX.emplace(std::move(aInputX)); + aSplineY.emplace(std::move(aInputY)); } else // generate the kind "natural spline" { double fXDerivation = std::numeric_limits<double>::infinity(); double fYDerivation = std::numeric_limits<double>::infinity(); - aSplineX.reset(new lcl_SplineCalculation( std::move(aInputX), fXDerivation, fXDerivation )); - aSplineY.reset(new lcl_SplineCalculation( std::move(aInputY), fYDerivation, fYDerivation )); + aSplineX.emplace(std::move(aInputX), fXDerivation, fXDerivation); + aSplineY.emplace(std::move(aInputY), fYDerivation, fYDerivation); } // fill result polygon with calculated values @@ -690,29 +677,23 @@ void SplineCalculater::CalculateBSplines( continue; // need at least 2 points, degree p needs at least n+1 points // next piece of series - std::unique_ptr<double[]> t(new double [n+1]); - if (!createParameterT(aPointsIn, t.get())) + std::vector<double> t(n + 1); + if (!createParameterT(aPointsIn, t.data())) { continue; // next piece of series } lcl_tSizeType m = n + p + 1; - std::unique_ptr<double[]> u(new double [m+1]); - createKnotVector(n, p, t.get(), u.get()); + std::vector<double> u(m + 1); + createKnotVector(n, p, t.data(), u.data()); // The matrix N contains the B-spline basis functions applied to parameters. // In each row only p+1 adjacent elements are non-zero. The starting // column in a higher row is equal or greater than in the lower row. // To store this matrix the non-zero elements are shifted to column 0 // and the amount of shifting is remembered in an array. - std::unique_ptr<double*[]> aMatN(new double*[n+1]); - for (lcl_tSizeType row = 0; row <=n; ++row) - { - aMatN[row] = new double[p+1]; - for (sal_uInt32 col = 0; col <= p; ++col) - aMatN[row][col] = 0.0; - } - std::unique_ptr<lcl_tSizeType[]> aShift(new lcl_tSizeType[n+1]); + std::vector<std::vector<double>> aMatN(n + 1, std::vector<double>(p + 1)); + std::vector<lcl_tSizeType> aShift(n + 1); aMatN[0][0] = 1.0; //all others are zero aShift[0] = 0; aMatN[n][0] = 1.0; @@ -732,7 +713,7 @@ void SplineCalculater::CalculateBSplines( // index in reduced matrix aMatN = (index in full matrix N) - (i-p) aShift[k] = i - p; - applyNtoParameterT(i, t[k], p, u.get(), aMatN[k]); + applyNtoParameterT(i, t[k], p, u.data(), aMatN[k].data()); } // next row k // Get matrix C of control points from the matrix equation aMatN * C = aPointsIn @@ -742,9 +723,6 @@ void SplineCalculater::CalculateBSplines( lcl_tSizeType c = 0; // true column index double fDivisor = 1.0; // used for diagonal element double fEliminate = 1.0; // used for the element, that will become zero - double fHelp; - tPointType aHelp; - lcl_tSizeType nHelp; // used in triangle change bool bIsSuccessful = true; for (c = 0 ; c <= n && bIsSuccessful; ++c) { @@ -764,18 +742,9 @@ void SplineCalculater::CalculateBSplines( // exchange total row r with total row c if necessary if (r != c) { - for ( sal_uInt32 i = 0; i <= p ; ++i) - { - fHelp = aMatN[r][i]; - aMatN[r][i] = aMatN[c][i]; - aMatN[c][i] = fHelp; - } - aHelp = aPointsIn[r]; - aPointsIn[r] = aPointsIn[c]; - aPointsIn[c] = aHelp; - nHelp = aShift[r]; - aShift[r] = aShift[c]; - aShift[c] = nHelp; + std::swap( aMatN[r], aMatN[c] ); + std::swap( aPointsIn[r], aPointsIn[c] ); + std::swap( aShift[r], aShift[c] ); } // divide row c, so that element(c,c) becomes 1 @@ -843,7 +812,7 @@ void SplineCalculater::CalculateBSplines( pNew[nNewSize -1 ].PositionX = aPointsIn[n].first; pNew[nNewSize -1 ].PositionY = aPointsIn[n].second; pNew[nNewSize -1 ].PositionZ = fZCoordinate; - std::unique_ptr<double[]> aP(new double[m+1]); + std::vector<double> aP(m + 1); lcl_tSizeType nLow = 0; for ( lcl_tSizeType nTIndex = 0; nTIndex <= n-1; ++nTIndex) { @@ -895,10 +864,6 @@ void SplineCalculater::CalculateBSplines( } } } - for (lcl_tSizeType row = 0; row <=n; ++row) - { - delete[] aMatN[row]; - } } // next piece of the series } diff --git a/chart2/source/view/charttypes/VSeriesPlotter.cxx b/chart2/source/view/charttypes/VSeriesPlotter.cxx index af54871ff49d..b9773494d785 100644 --- a/chart2/source/view/charttypes/VSeriesPlotter.cxx +++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx @@ -17,6 +17,7 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include <cstddef> #include <limits> #include <memory> #include <VSeriesPlotter.hxx> @@ -25,18 +26,23 @@ #include <ShapeFactory.hxx> #include <Diagram.hxx> #include <BaseCoordinateSystem.hxx> +#include <DataSeries.hxx> +#include <DataSeriesProperties.hxx> #include <CommonConverters.hxx> #include <ExplicitCategoriesProvider.hxx> +#include <FormattedString.hxx> #include <ObjectIdentifier.hxx> #include <StatisticsHelper.hxx> #include <PlottingPositionHelper.hxx> #include <LabelPositionHelper.hxx> +#include <ChartType.hxx> #include <ChartTypeHelper.hxx> #include <Clipping.hxx> #include <servicenames_charttypes.hxx> #include <NumberFormatterWrapper.hxx> #include <DataSeriesHelper.hxx> +#include <RegressionCurveModel.hxx> #include <RegressionCurveHelper.hxx> #include <VLegendSymbolFactory.hxx> #include <FormattedStringHelper.hxx> @@ -61,9 +67,9 @@ #include <com/sun/star/chart/TimeUnit.hpp> #include <com/sun/star/chart2/MovingAverageType.hpp> #include <com/sun/star/chart2/XDataPointCustomLabelField.hpp> -#include <com/sun/star/chart2/XRegressionCurveContainer.hpp> #include <com/sun/star/container/XChild.hpp> #include <com/sun/star/chart2/RelativePosition.hpp> +#include <o3tl/safeint.hxx> #include <tools/color.hxx> #include <tools/UnitConversion.hxx> #include <rtl/ustrbuf.hxx> @@ -71,13 +77,13 @@ #include <basegfx/vector/b2dvector.hxx> #include <com/sun/star/drawing/LineStyle.hpp> #include <com/sun/star/util/XCloneable.hpp> -#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> #include <unotools/localedatawrapper.hxx> #include <comphelper/sequence.hxx> +#include <utility> #include <vcl/svapp.hxx> #include <vcl/settings.hxx> -#include <tools/diagnose_ex.h> +#include <comphelper/diagnose_ex.hxx> #include <sal/log.hxx> #include <functional> @@ -90,6 +96,7 @@ namespace chart { using namespace ::com::sun::star; using namespace ::com::sun::star::chart; using namespace ::com::sun::star::chart2; +using namespace ::chart::DataSeriesProperties; using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::Sequence; @@ -137,12 +144,11 @@ sal_Int32 VDataSeriesGroup::getSeriesCount() const return m_aSeriesVector.size(); } -VSeriesPlotter::VSeriesPlotter( const uno::Reference<XChartType>& xChartTypeModel +VSeriesPlotter::VSeriesPlotter( rtl::Reference<ChartType> xChartTypeModel , sal_Int32 nDimensionCount, bool bCategoryXAxis ) : PlotterBase( nDimensionCount ) , m_pMainPosHelper( nullptr ) - , m_xChartTypeModel(xChartTypeModel) - , m_xChartTypeModelProps( uno::Reference< beans::XPropertySet >::query( xChartTypeModel )) + , m_xChartTypeModel(std::move(xChartTypeModel)) , m_bCategoryXAxis(bCategoryXAxis) , m_nTimeResolution(css::chart::TimeUnit::DAY) , m_aNullDate(30,12,1899) @@ -193,7 +199,7 @@ void VSeriesPlotter::addSeries( std::unique_ptr<VDataSeries> pSeries, sal_Int32 pSeries->setXValuesIfNone( m_pExplicitCategoriesProvider->getOriginalCategories() ); } - if(zSlot<0 || zSlot>=static_cast<sal_Int32>(m_aZSlots.size())) + if(zSlot<0 || o3tl::make_unsigned(zSlot)>=m_aZSlots.size()) { //new z slot std::vector< VDataSeriesGroup > aZSlot; @@ -205,7 +211,7 @@ void VSeriesPlotter::addSeries( std::unique_ptr<VDataSeries> pSeries, sal_Int32 //existing zslot std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[zSlot]; - if(xSlot<0 || xSlot>=static_cast<sal_Int32>(rXSlots.size())) + if(xSlot<0 || o3tl::make_unsigned(xSlot)>=rXSlots.size()) { //append the series to already existing x series rXSlots.emplace_back( std::move(pSeries) ); @@ -513,8 +519,8 @@ rtl::Reference<SvxShapeText> VSeriesPlotter::createDataLabel( const rtl::Referen OUString aRole; if ( m_xChartTypeModel ) aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel(); - const uno::Reference< XDataSeries >& xSeries( rDataSeries.getModel() ); - pTextList[i] = DataSeriesHelper::getDataSeriesLabel( xSeries, aRole ); + const rtl::Reference< DataSeries >& xSeries( rDataSeries.getModel() ); + pTextList[i] = xSeries->getLabelForRole( aRole ); break; } case DataPointCustomLabelFieldType_PERCENTAGE: @@ -571,8 +577,8 @@ rtl::Reference<SvxShapeText> VSeriesPlotter::createDataLabel( const rtl::Referen OUString aRole; if ( m_xChartTypeModel ) aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel(); - const uno::Reference< XDataSeries >& xSeries( rDataSeries.getModel() ); - pTextList[1] = DataSeriesHelper::getDataSeriesLabel( xSeries, aRole ); + const rtl::Reference< DataSeries >& xSeries( rDataSeries.getModel() ); + pTextList[1] = xSeries->getLabelForRole( aRole ); } if( pLabel->ShowNumber ) @@ -592,7 +598,7 @@ rtl::Reference<SvxShapeText> VSeriesPlotter::createDataLabel( const rtl::Referen } } - for( auto const & line : std::as_const(aTextList) ) + for (auto const& line : aTextList) { if( !line.isEmpty() ) { @@ -698,26 +704,20 @@ rtl::Reference<SvxShapeText> VSeriesPlotter::createDataLabel( const rtl::Referen { xTextShape->setPosition(aRelPos); if( !m_xChartTypeModel->getChartType().equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE) && - rDataSeries.getPropertiesOfSeries()->getPropertyValue( "ShowCustomLeaderLines" ).get<sal_Bool>()) + // "ShowCustomLeaderLines" + rDataSeries.getModel()->getFastPropertyValue( PROP_DATASERIES_SHOW_CUSTOM_LEADERLINES ).get<sal_Bool>()) { + const basegfx::B2IRectangle aRect( + BaseGFXHelper::makeRectangle(aRelPos, xTextShape->getSize())); sal_Int32 nX1 = rScreenPosition2D.X; sal_Int32 nY1 = rScreenPosition2D.Y; - sal_Int32 nX2 = nX1; - sal_Int32 nY2 = nY1; - ::basegfx::B2IRectangle aRect(BaseGFXHelper::makeRectangle(aRelPos, xTextShape->getSize())); - if (nX1 < aRelPos.X) - nX2 = aRelPos.X; - else if (nX1 > aRect.getMaxX()) - nX2 = aRect.getMaxX(); - - if (nY1 < aRect.getMinY()) - nY2 = aRect.getMinY(); - else if (nY1 > aRect.getMaxY()) - nY2 = aRect.getMaxY(); + const sal_Int32 nX2 = std::clamp(nX1, aRelPos.X, aRect.getMaxX()); + const sal_Int32 nY2 = std::clamp(nY1, aRect.getMinY(), aRect.getMaxY()); //when the line is very short compared to the page size don't create one ::basegfx::B2DVector aLength(nX1 - nX2, nY1 - nY2); - double fPageDiagonaleLength = sqrt(double(m_aPageReferenceSize.Width)*double(m_aPageReferenceSize.Width) + double(m_aPageReferenceSize.Height)*double(m_aPageReferenceSize.Height)); + double fPageDiagonaleLength + = std::hypot(m_aPageReferenceSize.Width, m_aPageReferenceSize.Height); if ((aLength.getLength() / fPageDiagonaleLength) >= 0.01) { drawing::PointSequenceSequence aPoints{ { {nX1, nY1}, {nX2, nY2} } }; @@ -731,13 +731,11 @@ rtl::Reference<SvxShapeText> VSeriesPlotter::createDataLabel( const rtl::Referen // in case legend symbol has to be displayed, text shape position is // slightly changed. - const awt::Point aUnrotatedTextPos(xTextShape->getPosition()); if( xSymbol.is() ) { - const awt::Point aOldTextPos( xTextShape->getPosition() ); - awt::Point aNewTextPos( aOldTextPos ); + awt::Point aNewTextPos(xTextShape->getPosition()); - awt::Point aSymbolPosition( aUnrotatedTextPos ); + awt::Point aSymbolPosition(aNewTextPos); awt::Size aSymbolSize( xSymbol->getSize() ); awt::Size aTextSize = xTextShape->getSize(); @@ -1041,7 +1039,21 @@ void VSeriesPlotter::createErrorBar( fLocalX-=fLength; aNegative = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true ); } - bCreateNegativeBorder = m_pPosHelper->isLogicVisible( fLocalX, fLocalY, fZ); + if (std::isfinite(aNegative.PositionX) && + std::isfinite(aNegative.PositionY) && + std::isfinite(aNegative.PositionZ)) { + bCreateNegativeBorder = m_pPosHelper->isLogicVisible( fLocalX, fLocalY, fZ); + } else { + // If error bars result in a numerical problem (e.g., an + // error bar on a logarithmic chart that results in a point + // <= 0) then just turn off the error bar. + // + // TODO: This perhaps should display a warning, so the user + // knows why a bar is not appearing. + // TODO: This test could also be added to the positive case, + // though a numerical overflow there is less likely. + bShowNegative = false; + } } else bShowNegative = false; @@ -1088,10 +1100,7 @@ void VSeriesPlotter::addErrorBorder( ,const rtl::Reference<SvxShapeGroupAnyD>& rTarget ,const uno::Reference< beans::XPropertySet >& rErrorBorderProp ) { - std::vector<std::vector<css::drawing::Position3D>> aPoly; - sal_Int32 nSequenceIndex = 0; - AddPointToPoly( aPoly, rPos0, nSequenceIndex ); - AddPointToPoly( aPoly, rPos1, nSequenceIndex ); + std::vector<std::vector<css::drawing::Position3D>> aPoly { { rPos0, rPos1} }; rtl::Reference<SvxShapePolyPolygon> xShape = ShapeFactory::createLine2D( rTarget, aPoly ); PropertyMapper::setMappedProperties( *xShape, rErrorBorderProp, @@ -1293,24 +1302,23 @@ void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries const & rVDataSer { if(m_nDimension!=2) return; - uno::Reference< XRegressionCurveContainer > xContainer( rVDataSeries.getModel(), uno::UNO_QUERY ); + rtl::Reference< DataSeries > xContainer( rVDataSeries.getModel() ); if(!xContainer.is()) return; if (!m_pPosHelper) return; - uno::Sequence< uno::Reference< XRegressionCurve > > aCurveList = xContainer->getRegressionCurves(); + const std::vector< rtl::Reference< ::chart::RegressionCurveModel > > & aCurveList = xContainer->getRegressionCurves2(); - for(sal_Int32 nN=0; nN<aCurveList.getLength(); nN++) + for(std::size_t nN=0; nN<aCurveList.size(); nN++) { - uno::Reference< XRegressionCurveCalculator > xCalculator( aCurveList[nN]->getCalculator() ); + const auto & rCurve = aCurveList[nN]; + uno::Reference< XRegressionCurveCalculator > xCalculator( rCurve->getCalculator() ); if( !xCalculator.is()) continue; - uno::Reference< beans::XPropertySet > xProperties( aCurveList[nN], uno::UNO_QUERY ); - - bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurveList[nN] ); + bool bAverageLine = RegressionCurveHelper::isMeanValueLine( rCurve ); sal_Int32 aDegree = 2; sal_Int32 aPeriod = 2; @@ -1320,16 +1328,16 @@ void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries const & rVDataSer bool bForceIntercept = false; double aInterceptValue = 0.0; - if ( xProperties.is() && !bAverageLine ) + if ( !bAverageLine ) { - xProperties->getPropertyValue( "PolynomialDegree") >>= aDegree; - xProperties->getPropertyValue( "MovingAveragePeriod") >>= aPeriod; - xProperties->getPropertyValue( "MovingAverageType") >>= aMovingAverageType; - xProperties->getPropertyValue( "ExtrapolateForward") >>= aExtrapolateForward; - xProperties->getPropertyValue( "ExtrapolateBackward") >>= aExtrapolateBackward; - xProperties->getPropertyValue( "ForceIntercept") >>= bForceIntercept; + rCurve->getPropertyValue( "PolynomialDegree") >>= aDegree; + rCurve->getPropertyValue( "MovingAveragePeriod") >>= aPeriod; + rCurve->getPropertyValue( "MovingAverageType") >>= aMovingAverageType; + rCurve->getPropertyValue( "ExtrapolateForward") >>= aExtrapolateForward; + rCurve->getPropertyValue( "ExtrapolateBackward") >>= aExtrapolateBackward; + rCurve->getPropertyValue( "ForceIntercept") >>= bForceIntercept; if (bForceIntercept) - xProperties->getPropertyValue( "InterceptValue") >>= aInterceptValue; + rCurve->getPropertyValue( "InterceptValue") >>= aInterceptValue; } double fChartMinX = m_pPosHelper->getLogicMinX(); @@ -1421,7 +1429,7 @@ void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries const & rVDataSer if( aRegressionPoly.SequenceX.hasElements() && aRegressionPoly.SequenceX[0].hasElements() ) { VLineProperties aVLineProperties; - aVLineProperties.initFromPropertySet( xProperties ); + aVLineProperties.initFromPropertySet( rCurve ); //create an extra group shape for each curve for selection handling rtl::Reference<SvxShapeGroupAnyD> xRegressionGroupShapes = @@ -1433,7 +1441,7 @@ void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries const & rVDataSer } // curve equation and correlation coefficient - uno::Reference< beans::XPropertySet > xEquationProperties( aCurveList[nN]->getEquationProperties()); + uno::Reference< beans::XPropertySet > xEquationProperties( rCurve->getEquationProperties()); if( xEquationProperties.is()) { createRegressionCurveEquationShapes( @@ -2289,12 +2297,83 @@ OUString VSeriesPlotter::getCategoryName( sal_Int32 nPointIndex ) const return OUString(); } -uno::Sequence< OUString > VSeriesPlotter::getSeriesNames() const +namespace { +// The following it to support rendering order for combo charts. A chart type +// with a lower rendering order is rendered before (i.e., behind) a chart with a +// higher rendering order. The rendering orders are based on rough guesses about +// how much one chart (type) will obscure another chart (type). The intent is to +// minimize obscuring of data, by putting charts that generally cover more +// pixels (e.g., area charts) behind ones that generally cover fewer (e.g., line +// charts). +struct ROrderPair +{ + ROrderPair(OUString n, sal_Int32 r) : chartName(n), renderOrder(r) {} + + OUString chartName; + sal_Int32 renderOrder; +}; + +const ROrderPair pairList[] = { + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_AREA, 0), + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_BAR, 6), // bar & column are same + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN, 6), + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_LINE, 8), + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER, 5), + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_PIE, 1), + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_NET, 3), + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET, 2), + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK, 7), + ROrderPair(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE, 4) +}; +} // unnamed + +sal_Int32 VSeriesPlotter::getRenderOrder() const +{ + OUString aChartType = m_xChartTypeModel->getChartType(); + for (const auto& elem : pairList) { + if (aChartType.equalsIgnoreAsciiCase(elem.chartName)) { + return elem.renderOrder; + } + } + SAL_WARN("chart2", "Unsupported chart type in getRenderOrder()"); + return 0; +} + +std::vector<VDataSeries const*> VSeriesPlotter::getAllSeries() const +{ + std::vector<VDataSeries const*> aAllSeries; + for (std::vector<VDataSeriesGroup> const & rXSlot : m_aZSlots) + { + for(VDataSeriesGroup const & rGroup : rXSlot) + { + for (std::unique_ptr<VDataSeries> const & p : rGroup.m_aSeriesVector) + aAllSeries.push_back(p.get()); + } + } + return aAllSeries; +} + + +std::vector<VDataSeries*> VSeriesPlotter::getAllSeries() +{ + std::vector<VDataSeries*> aAllSeries; + for (std::vector<VDataSeriesGroup> const & rXSlot : m_aZSlots) + { + for(VDataSeriesGroup const & rGroup : rXSlot) + { + for (std::unique_ptr<VDataSeries> const & p : rGroup.m_aSeriesVector) + aAllSeries.push_back(p.get()); + } + } + return aAllSeries; +} + +uno::Sequence<OUString> VSeriesPlotter::getSeriesNames() const { std::vector<OUString> aRetVector; OUString aRole; - if( m_xChartTypeModel.is() ) + if (m_xChartTypeModel.is()) aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel(); for (auto const& rGroup : m_aZSlots) @@ -2305,10 +2384,10 @@ uno::Sequence< OUString > VSeriesPlotter::getSeriesNames() const if (!rSeriesGroup.m_aSeriesVector.empty()) { VDataSeries const * pSeries = rSeriesGroup.m_aSeriesVector[0].get(); - uno::Reference< XDataSeries > xSeries( pSeries ? pSeries->getModel() : nullptr ); + rtl::Reference< DataSeries > xSeries( pSeries ? pSeries->getModel() : nullptr ); if( xSeries.is() ) { - OUString aSeriesName( DataSeriesHelper::getDataSeriesLabel( xSeries, aRole ) ); + OUString aSeriesName( xSeries->getLabelForRole( aRole ) ); aRetVector.push_back( aSeriesName ); } } @@ -2317,6 +2396,25 @@ uno::Sequence< OUString > VSeriesPlotter::getSeriesNames() const return comphelper::containerToSequence( aRetVector ); } +uno::Sequence<OUString> VSeriesPlotter::getAllSeriesNames() const +{ + std::vector<OUString> aRetVector; + + OUString aRole; + if (m_xChartTypeModel.is()) + aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel(); + + for (VDataSeries const* pSeries : getAllSeries()) + { + if (pSeries) + { + OUString aSeriesName(pSeries->getModel()->getLabelForRole(aRole)); + aRetVector.push_back(aSeriesName); + } + } + return comphelper::containerToSequence(aRetVector); +} + void VSeriesPlotter::setPageReferenceSize( const css::awt::Size & rPageRefSize ) { m_aPageReferenceSize = rPageRefSize; @@ -2388,7 +2486,8 @@ std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntries( if (!pSeries) continue; - if (!pSeries->getPropertiesOfSeries()->getPropertyValue("ShowLegendEntry").get<sal_Bool>()) + // "ShowLegendEntry" + if (!pSeries->getModel()->getFastPropertyValue(PROP_DATASERIES_SHOW_LEGEND_ENTRY).get<sal_Bool>()) { continue; } @@ -2434,18 +2533,45 @@ std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntries( return aResult; } -std::vector<VDataSeries*> VSeriesPlotter::getAllSeries() +std::vector<ViewLegendSymbol> VSeriesPlotter::createSymbols(const awt::Size& rEntryKeyAspectRatio + , const rtl::Reference<SvxShapeGroupAnyD>& xTarget + , const Reference<uno::XComponentContext>& xContext) { - std::vector<VDataSeries*> aAllSeries; - for (std::vector<VDataSeriesGroup> const & rXSlot : m_aZSlots) + std::vector<ViewLegendSymbol> aResult; + + if( xTarget.is() ) { - for(VDataSeriesGroup const & rGroup : rXSlot) + bool bBreak = false; + bool bFirstSeries = true; + + for (std::vector<VDataSeriesGroup> const & rGroupVector : m_aZSlots) { - for (std::unique_ptr<VDataSeries> const & p : rGroup.m_aSeriesVector) - aAllSeries.push_back(p.get()); + for (VDataSeriesGroup const & rGroup : rGroupVector) + { + for (std::unique_ptr<VDataSeries> const & pSeries : rGroup.m_aSeriesVector) + { + if (!pSeries) + continue; + + std::vector<ViewLegendSymbol> aSeriesSymbols = createSymbolsForSeries(rEntryKeyAspectRatio, *pSeries, xTarget, xContext); + + //add series entries to the result now + + // use only the first series if VaryColorsByPoint is set for the first series + if (bFirstSeries && pSeries->isVaryColorsByPoint()) + bBreak = true; + + bFirstSeries = false; + + aResult.insert(aResult.end(), aSeriesSymbols.begin(), aSeriesSymbols.end()); + } + if (bBreak) + return aResult; + } } } - return aAllSeries; + + return aResult; } namespace @@ -2468,19 +2594,11 @@ bool lcl_HasVisibleLine( const uno::Reference< beans::XPropertySet >& xProps, bo bool lcl_HasRegressionCurves( const VDataSeries& rSeries, bool& rbHasDashedLine ) { bool bHasRegressionCurves = false; - Reference< XRegressionCurveContainer > xRegrCont( rSeries.getModel(), uno::UNO_QUERY ); - if( xRegrCont.is()) + rtl::Reference< DataSeries > xRegrCont( rSeries.getModel() ); + for( const rtl::Reference< RegressionCurveModel > & rCurve : xRegrCont->getRegressionCurves2() ) { - Sequence< Reference< XRegressionCurve > > aCurves( xRegrCont->getRegressionCurves() ); - sal_Int32 i = 0, nCount = aCurves.getLength(); - for( i=0; i<nCount; ++i ) - { - if( aCurves[i].is() ) - { - bHasRegressionCurves = true; - lcl_HasVisibleLine( uno::Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ), rbHasDashedLine ); - } - } + bHasRegressionCurves = true; + lcl_HasVisibleLine( rCurve, rbHasDashedLine ); } return bHasRegressionCurves; } @@ -2646,10 +2764,11 @@ std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries( CHART2_SERVICE_NAME_CHARTTYPE_PIE); try { - if (bIsPie && m_xChartTypeModelProps.is()) + if (bIsPie) { bool bDonut = false; - if ((m_xChartTypeModelProps->getPropertyValue("UseRings") >>= bDonut) && bDonut) + // "UseRings" + if ((m_xChartTypeModel->getFastPropertyValue(PROP_PIECHARTTYPE_USE_RINGS) >>= bDonut) && bDonut) bIsPie = false; } } @@ -2665,7 +2784,8 @@ std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries( Sequence<sal_Int32> deletedLegendEntries; try { - rSeries.getPropertiesOfSeries()->getPropertyValue("DeletedLegendEntries") >>= deletedLegendEntries; + // "DeletedLegendEntries" + rSeries.getModel()->getFastPropertyValue(PROP_DATASERIES_DELETED_LEGEND_ENTRIES) >>= deletedLegendEntries; } catch (const uno::Exception&) { @@ -2673,7 +2793,7 @@ std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries( for( sal_Int32 nIdx=0; nIdx<aCategoryNames.getLength(); ++nIdx ) { bool deletedLegendEntry = false; - for (const auto& deletedLegendEntryIdx : std::as_const(deletedLegendEntries)) + for (const auto& deletedLegendEntryIdx : deletedLegendEntries) { if (nIdx == deletedLegendEntryIdx) { @@ -2706,7 +2826,7 @@ std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries( aLabelText = aCategoryNames[nIdx]; if( xShape.is() || !aLabelText.isEmpty() ) { - aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties ); + aEntry.xLabel = FormattedStringHelper::createFormattedString( aLabelText, xTextProperties ); aResult.push_back(aEntry); } } @@ -2731,8 +2851,8 @@ std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries( } // label - aLabelText = DataSeriesHelper::getDataSeriesLabel( rSeries.getModel(), m_xChartTypeModel.is() ? m_xChartTypeModel->getRoleOfSequenceForSeriesLabel() : "values-y"); - aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties ); + aLabelText = rSeries.getModel()->getLabelForRole( m_xChartTypeModel.is() ? m_xChartTypeModel->getRoleOfSequenceForSeriesLabel() : "values-y"); + aEntry.xLabel = FormattedStringHelper::createFormattedString( aLabelText, xTextProperties ); aResult.push_back(aEntry); } @@ -2742,44 +2862,41 @@ std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries( if (!ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel, m_nDimension )) return aResult; - Reference< XRegressionCurveContainer > xRegrCont( rSeries.getModel(), uno::UNO_QUERY ); + rtl::Reference< DataSeries > xRegrCont = rSeries.getModel(); if( xRegrCont.is()) { - Sequence< Reference< XRegressionCurve > > aCurves( xRegrCont->getRegressionCurves()); - sal_Int32 i = 0, nCount = aCurves.getLength(); + const std::vector< rtl::Reference< RegressionCurveModel > > & aCurves = xRegrCont->getRegressionCurves2(); + sal_Int32 i = 0, nCount = aCurves.size(); for( i=0; i<nCount; ++i ) { - if( aCurves[i].is() ) + //label + OUString aResStr( RegressionCurveHelper::getUINameForRegressionCurve( aCurves[i] ) ); + replaceParamterInString( aResStr, u"%SERIESNAME", aLabelText ); + aEntry.xLabel = FormattedStringHelper::createFormattedString( aResStr, xTextProperties ); + + // symbol + rtl::Reference<SvxShapeGroup> xSymbolGroup(ShapeFactory::createGroup2D( xTarget )); + + // create the symbol + rtl::Reference<SvxShapeGroup> xShape = VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio, + xSymbolGroup, LegendSymbolStyle::Line, + aCurves[i], + VLegendSymbolFactory::PropertyType::Line, uno::Any() ); + + // set CID to symbol for selection + if( xShape.is()) { - //label - OUString aResStr( RegressionCurveHelper::getUINameForRegressionCurve( aCurves[i] ) ); - replaceParamterInString( aResStr, "%SERIESNAME", aLabelText ); - aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aResStr, xTextProperties ); - - // symbol - rtl::Reference<SvxShapeGroup> xSymbolGroup(ShapeFactory::createGroup2D( xTarget )); - - // create the symbol - rtl::Reference<SvxShapeGroup> xShape = VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio, - xSymbolGroup, LegendSymbolStyle::Line, - Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ), - VLegendSymbolFactory::PropertyType::Line, uno::Any() ); - - // set CID to symbol for selection - if( xShape.is()) - { - aEntry.xSymbol = xSymbolGroup; - - bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurves[i] ); - ObjectType eObjectType = bAverageLine ? OBJECTTYPE_DATA_AVERAGE_LINE : OBJECTTYPE_DATA_CURVE; - OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( eObjectType, i ) ); - aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) ); - OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle ); - ShapeFactory::setShapeName( xShape, aCID ); - } + aEntry.xSymbol = xSymbolGroup; - aResult.push_back(aEntry); + bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurves[i] ); + ObjectType eObjectType = bAverageLine ? OBJECTTYPE_DATA_AVERAGE_LINE : OBJECTTYPE_DATA_CURVE; + OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( eObjectType, i ) ); + aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) ); + OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle ); + ShapeFactory::setShapeName( xShape, aCID ); } + + aResult.push_back(aEntry); } } } @@ -2790,8 +2907,42 @@ std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries( return aResult; } +std::vector<ViewLegendSymbol> VSeriesPlotter::createSymbolsForSeries( + const awt::Size& rEntryKeyAspectRatio + , const VDataSeries& rSeries + , const rtl::Reference<SvxShapeGroupAnyD>& xTarget + , const Reference<uno::XComponentContext>& xContext) +{ + std::vector<ViewLegendSymbol> aResult; + + if (!(xTarget.is() && xContext.is())) + return aResult; + + try + { + ViewLegendSymbol aEntry; + // symbol + rtl::Reference<SvxShapeGroup> xSymbolGroup(ShapeFactory::createGroup2D(xTarget)); + + // create the symbol + rtl::Reference<SvxShapeGroup> xShape = createLegendSymbolForSeries(rEntryKeyAspectRatio, rSeries, xSymbolGroup ); + + // set CID to symbol for selection + if (xShape.is()) + { + aEntry.xSymbol = xSymbolGroup; + aResult.push_back(aEntry); + } + } + catch (const uno::Exception &) + { + DBG_UNHANDLED_EXCEPTION("chart2" ); + } + return aResult; +} + VSeriesPlotter* VSeriesPlotter::createSeriesPlotter( - const uno::Reference<XChartType>& xChartTypeModel + const rtl::Reference<ChartType>& xChartTypeModel , sal_Int32 nDimensionCount , bool bExcludingPositioning ) { |