tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / chart2 / source / view / charttypes / AreaChart.cxx
blobf5a73b966c4a0a2cbbbc6b99d2b2f7431d6a092a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "AreaChart.hxx"
21 #include <PlottingPositionHelper.hxx>
22 #include <ShapeFactory.hxx>
23 #include <CommonConverters.hxx>
24 #include <ExplicitCategoriesProvider.hxx>
25 #include <ObjectIdentifier.hxx>
26 #include "Splines.hxx"
27 #include <ChartType.hxx>
28 #include <ChartTypeHelper.hxx>
29 #include <LabelPositionHelper.hxx>
30 #include <Clipping.hxx>
31 #include <Stripe.hxx>
32 #include <DateHelper.hxx>
33 #include <unonames.hxx>
35 #include <com/sun/star/chart2/Symbol.hpp>
36 #include <com/sun/star/chart/DataLabelPlacement.hpp>
37 #include <com/sun/star/chart/MissingValueTreatment.hpp>
39 #include <sal/log.hxx>
40 #include <o3tl/safeint.hxx>
41 #include <osl/diagnose.h>
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <officecfg/Office/Compatibility.hxx>
45 #include <officecfg/Office/Chart.hxx>
47 #include <limits>
49 namespace chart
51 using namespace ::com::sun::star;
52 using namespace ::com::sun::star::chart2;
54 AreaChart::AreaChart( const rtl::Reference<ChartType>& xChartTypeModel
55 , sal_Int32 nDimensionCount
56 , bool bCategoryXAxis
57 , bool bNoArea
59 : VSeriesPlotter( xChartTypeModel, nDimensionCount, bCategoryXAxis )
60 , m_bArea(!bNoArea)
61 , m_bLine(bNoArea)
62 , m_bSymbol( ChartTypeHelper::isSupportingSymbolProperties(xChartTypeModel,nDimensionCount) )
63 , m_eCurveStyle(CurveStyle_LINES)
64 , m_nCurveResolution(20)
65 , m_nSplineOrder(3)
67 PlotterBase::m_pPosHelper = &m_aMainPosHelper;
68 VSeriesPlotter::m_pMainPosHelper = &m_aMainPosHelper;
70 m_pMainPosHelper->AllowShiftXAxisPos(true);
71 m_pMainPosHelper->AllowShiftZAxisPos(true);
73 try
75 if( m_xChartTypeModel.is() )
77 m_xChartTypeModel->getPropertyValue(CHART_UNONAME_CURVE_STYLE) >>= m_eCurveStyle;
78 m_xChartTypeModel->getPropertyValue(CHART_UNONAME_CURVE_RESOLUTION) >>= m_nCurveResolution;
79 m_xChartTypeModel->getPropertyValue(CHART_UNONAME_SPLINE_ORDER) >>= m_nSplineOrder;
82 catch( uno::Exception& e )
84 //the above properties are not supported by all charttypes supported by this class (e.g. area or net chart)
85 //in that cases this exception is ok
86 e.Context.is();//to have debug information without compilation warnings
90 AreaChart::~AreaChart()
94 bool AreaChart::isSeparateStackingForDifferentSigns( sal_Int32 /*nDimensionIndex*/ )
96 // no separate stacking in all types of line/area charts
97 return false;
100 LegendSymbolStyle AreaChart::getLegendSymbolStyle()
102 if( m_bArea || m_nDimension == 3 )
103 return LegendSymbolStyle::Box;
104 return LegendSymbolStyle::Line;
107 uno::Any AreaChart::getExplicitSymbol( const VDataSeries& rSeries, sal_Int32 nPointIndex )
109 uno::Any aRet;
111 Symbol* pSymbolProperties = rSeries.getSymbolProperties( nPointIndex );
112 if( pSymbolProperties )
114 aRet <<= *pSymbolProperties;
117 return aRet;
120 drawing::Direction3D AreaChart::getPreferredDiagramAspectRatio() const
122 drawing::Direction3D aRet(1,-1,1);
123 if( m_nDimension == 2 )
124 aRet = drawing::Direction3D(-1,-1,-1);
125 else if (m_pPosHelper)
127 drawing::Direction3D aScale( m_pPosHelper->getScaledLogicWidth() );
128 aRet.DirectionZ = aScale.DirectionZ*0.2;
129 if(aRet.DirectionZ>1.0)
130 aRet.DirectionZ=1.0;
131 if(aRet.DirectionZ>10)
132 aRet.DirectionZ=10;
134 return aRet;
137 void AreaChart::addSeries( std::unique_ptr<VDataSeries> pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot )
139 if( m_bArea && pSeries )
141 sal_Int32 nMissingValueTreatment = pSeries->getMissingValueTreatment();
142 if( nMissingValueTreatment == css::chart::MissingValueTreatment::LEAVE_GAP )
143 pSeries->setMissingValueTreatment( css::chart::MissingValueTreatment::USE_ZERO );
145 if( m_nDimension == 3 && !m_bCategoryXAxis )
147 //3D xy always deep
148 OSL_ENSURE( zSlot==-1,"3D xy charts should be deep stacked in model also" );
149 zSlot=-1;
150 xSlot=0;
151 ySlot=0;
153 VSeriesPlotter::addSeries( std::move(pSeries), zSlot, xSlot, ySlot );
156 static void lcl_removeDuplicatePoints( std::vector<std::vector<css::drawing::Position3D>>& rPolyPoly, PlottingPositionHelper& rPosHelper )
158 sal_Int32 nPolyCount = rPolyPoly.size();
159 if(!nPolyCount)
160 return;
162 // TODO we could do with without a temporary array
163 std::vector<std::vector<css::drawing::Position3D>> aTmp;
164 aTmp.resize(nPolyCount);
166 for( sal_Int32 nPolygonIndex = 0; nPolygonIndex<nPolyCount; nPolygonIndex++ )
168 std::vector<css::drawing::Position3D>* pOuterSource = &rPolyPoly[nPolygonIndex];
169 std::vector<css::drawing::Position3D>* pOuterTarget = &aTmp[nPolygonIndex];
171 sal_Int32 nPointCount = pOuterSource->size();
172 if( !nPointCount )
173 continue;
175 pOuterTarget->resize(nPointCount);
177 css::drawing::Position3D* pSource = pOuterSource->data();
178 css::drawing::Position3D* pTarget = pOuterTarget->data();
180 //copy first point
181 *pTarget=*pSource++;
182 sal_Int32 nTargetPointCount=1;
184 for( sal_Int32 nSource=1; nSource<nPointCount; nSource++ )
186 if( !rPosHelper.isSameForGivenResolution( pTarget->PositionX, pTarget->PositionY, pTarget->PositionZ
187 , pSource->PositionX, pSource->PositionY, pSource->PositionZ ) )
189 pTarget++;
190 *pTarget=*pSource;
191 nTargetPointCount++;
193 pSource++;
196 //free unused space
197 if( nTargetPointCount<nPointCount )
199 pOuterTarget->resize(nTargetPointCount);
202 pOuterSource->clear();
205 //free space
206 rPolyPoly.resize(nPolyCount);
208 rPolyPoly = std::move(aTmp);
211 bool AreaChart::create_stepped_line(
212 std::vector<std::vector<css::drawing::Position3D>> aStartPoly,
213 chart2::CurveStyle eCurveStyle,
214 PlottingPositionHelper const * pPosHelper,
215 std::vector<std::vector<css::drawing::Position3D>> &aPoly )
217 sal_uInt32 nOuterCount = aStartPoly.size();
218 if ( !nOuterCount )
219 return false;
221 std::vector<std::vector<css::drawing::Position3D>> aSteppedPoly;
222 aSteppedPoly.resize(nOuterCount);
224 auto pSequence = aSteppedPoly.data();
226 for( sal_uInt32 nOuter = 0; nOuter < nOuterCount; ++nOuter )
228 if( aStartPoly[nOuter].size() <= 1 )
229 continue; //we need at least two points
231 sal_uInt32 nMaxIndexPoints = aStartPoly[nOuter].size()-1; // is >1
232 sal_uInt32 nNewIndexPoints = 0;
233 if ( eCurveStyle==CurveStyle_STEP_START || eCurveStyle==CurveStyle_STEP_END)
234 nNewIndexPoints = nMaxIndexPoints * 2 + 1;
235 else
236 nNewIndexPoints = nMaxIndexPoints * 3 + 1;
238 const css::drawing::Position3D* pOld = aStartPoly[nOuter].data();
240 pSequence[nOuter].resize( nNewIndexPoints );
242 css::drawing::Position3D* pNew = pSequence[nOuter].data();
244 pNew[0] = pOld[0];
245 for( sal_uInt32 oi = 0; oi < nMaxIndexPoints; oi++ )
247 switch ( eCurveStyle )
249 case CurveStyle_STEP_START:
250 /** O
254 O-----+
256 // create the intermediate point
257 pNew[1+oi*2].PositionX = pOld[oi+1].PositionX;
258 pNew[1+oi*2].PositionY = pOld[oi].PositionY;
259 pNew[1+oi*2].PositionZ = pOld[oi].PositionZ;
260 // and now the normal one
261 pNew[1+oi*2+1] = pOld[oi+1];
262 break;
263 case CurveStyle_STEP_END:
264 /** +------O
270 // create the intermediate point
271 pNew[1+oi*2].PositionX = pOld[oi].PositionX;
272 pNew[1+oi*2].PositionY = pOld[oi+1].PositionY;
273 pNew[1+oi*2].PositionZ = pOld[oi].PositionZ;
274 // and now the normal one
275 pNew[1+oi*2+1] = pOld[oi+1];
276 break;
277 case CurveStyle_STEP_CENTER_X:
278 /** +--O
282 O--+
284 // create the first intermediate point
285 pNew[1+oi*3].PositionX = (pOld[oi].PositionX + pOld[oi+1].PositionX) / 2;
286 pNew[1+oi*3].PositionY = pOld[oi].PositionY;
287 pNew[1+oi*3].PositionZ = pOld[oi].PositionZ;
288 // create the second intermediate point
289 pNew[1+oi*3+1].PositionX = (pOld[oi].PositionX + pOld[oi+1].PositionX) / 2;
290 pNew[1+oi*3+1].PositionY = pOld[oi+1].PositionY;
291 pNew[1+oi*3+1].PositionZ = pOld[oi].PositionZ;
292 // and now the normal one
293 pNew[1+oi*3+2] = pOld[oi+1];
294 break;
295 case CurveStyle_STEP_CENTER_Y:
296 /** O
298 +-----+
302 // create the first intermediate point
303 pNew[1+oi*3].PositionX = pOld[oi].PositionX;
304 pNew[1+oi*3].PositionY = (pOld[oi].PositionY + pOld[oi+1].PositionY) / 2;
305 pNew[1+oi*3].PositionZ = pOld[oi].PositionZ;
306 // create the second intermediate point
307 pNew[1+oi*3+1].PositionX = pOld[oi+1].PositionX;
308 pNew[1+oi*3+1].PositionY = (pOld[oi].PositionY + pOld[oi+1].PositionY) / 2;
309 pNew[1+oi*3+1].PositionZ = pOld[oi].PositionZ;
310 // and now the normal one
311 pNew[1+oi*3+2] = pOld[oi+1];
312 break;
313 default:
314 // this should never be executed
315 OSL_FAIL("Unknown curvestyle in AreaChart::create_stepped_line");
319 Clipping::clipPolygonAtRectangle( aSteppedPoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
321 return true;
324 bool AreaChart::impl_createLine( VDataSeries* pSeries
325 , std::vector<std::vector<css::drawing::Position3D>> const * pSeriesPoly
326 , PlottingPositionHelper* pPosHelper )
328 //return true if a line was created successfully
329 rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShapeBackChild(pSeries, m_xSeriesTarget);
331 std::vector<std::vector<css::drawing::Position3D>> aPoly;
332 if(m_eCurveStyle==CurveStyle_CUBIC_SPLINES)
334 std::vector<std::vector<css::drawing::Position3D>> aSplinePoly;
335 SplineCalculater::CalculateCubicSplines( *pSeriesPoly, aSplinePoly, m_nCurveResolution );
336 lcl_removeDuplicatePoints( aSplinePoly, *pPosHelper );
337 Clipping::clipPolygonAtRectangle( aSplinePoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
339 else if(m_eCurveStyle==CurveStyle_B_SPLINES)
341 std::vector<std::vector<css::drawing::Position3D>> aSplinePoly;
342 SplineCalculater::CalculateBSplines( *pSeriesPoly, aSplinePoly, m_nCurveResolution, m_nSplineOrder );
343 lcl_removeDuplicatePoints( aSplinePoly, *pPosHelper );
344 Clipping::clipPolygonAtRectangle( aSplinePoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
346 else if (m_eCurveStyle==CurveStyle_STEP_START ||
347 m_eCurveStyle==CurveStyle_STEP_END ||
348 m_eCurveStyle==CurveStyle_STEP_CENTER_Y ||
349 m_eCurveStyle==CurveStyle_STEP_CENTER_X
352 if (!create_stepped_line(*pSeriesPoly, m_eCurveStyle, pPosHelper, aPoly))
354 return false;
357 else
358 { // default to creating a straight line
359 SAL_WARN_IF(m_eCurveStyle != CurveStyle_LINES, "chart2.areachart", "Unknown curve style");
360 Clipping::clipPolygonAtRectangle( *pSeriesPoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
363 if(!ShapeFactory::hasPolygonAnyLines(aPoly))
364 return false;
366 //transformation 3) -> 4)
367 pPosHelper->transformScaledLogicToScene( aPoly );
369 //create line:
370 rtl::Reference< SvxShape > xShape;
371 if(m_nDimension==3)
373 double fDepth = getTransformedDepth();
374 sal_Int32 nPolyCount = aPoly.size();
375 for(sal_Int32 nPoly=0;nPoly<nPolyCount;nPoly++)
377 sal_Int32 nPointCount = aPoly[nPoly].size();
378 for(sal_Int32 nPoint=0;nPoint<nPointCount-1;nPoint++)
380 drawing::Position3D aPoint1, aPoint2;
381 aPoint1 = aPoly[nPoly][nPoint+1];
382 aPoint2 = aPoly[nPoly][nPoint];
384 ShapeFactory::createStripe(xSeriesGroupShape_Shapes
385 , Stripe( aPoint1, aPoint2, fDepth )
386 , pSeries->getPropertiesOfSeries(), PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), true, 1 );
390 else //m_nDimension!=3
392 xShape = ShapeFactory::createLine2D( xSeriesGroupShape_Shapes, aPoly );
393 PropertyMapper::setMappedProperties( *xShape
394 , pSeries->getPropertiesOfSeries()
395 , PropertyMapper::getPropertyNameMapForLineSeriesProperties() );
396 //because of this name this line will be used for marking
397 ::chart::ShapeFactory::setShapeName(xShape, u"MarkHandles"_ustr);
399 return true;
402 bool AreaChart::impl_createArea( VDataSeries* pSeries
403 , std::vector<std::vector<css::drawing::Position3D>> const * pSeriesPoly
404 , std::vector<std::vector<css::drawing::Position3D>> const * pPreviousSeriesPoly
405 , PlottingPositionHelper const * pPosHelper )
407 //return true if an area was created successfully
409 rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShapeBackChild(pSeries, m_xSeriesTarget);
410 double zValue = pSeries->m_fLogicZPos;
412 std::vector<std::vector<css::drawing::Position3D>> aPoly( *pSeriesPoly );
413 //add second part to the polygon (grounding points or previous series points)
414 if(!pPreviousSeriesPoly)
416 double fMinX = pSeries->m_fLogicMinX;
417 double fMaxX = pSeries->m_fLogicMaxX;
418 double fY = pPosHelper->getBaseValueY();//logic grounding
419 if( m_nDimension==3 )
420 fY = pPosHelper->getLogicMinY();
422 //clip to scale
423 if(fMaxX<pPosHelper->getLogicMinX() || fMinX>pPosHelper->getLogicMaxX())
424 return false;//no visible shape needed
425 pPosHelper->clipLogicValues( &fMinX, &fY, nullptr );
426 pPosHelper->clipLogicValues( &fMaxX, nullptr, nullptr );
428 //apply scaling
430 pPosHelper->doLogicScaling( &fMinX, &fY, &zValue );
431 pPosHelper->doLogicScaling( &fMaxX, nullptr, nullptr );
434 AddPointToPoly( aPoly, drawing::Position3D( fMaxX,fY,zValue) );
435 AddPointToPoly( aPoly, drawing::Position3D( fMinX,fY,zValue) );
437 else
439 appendPoly( aPoly, *pPreviousSeriesPoly );
441 ShapeFactory::closePolygon(aPoly);
443 //apply clipping
445 std::vector<std::vector<css::drawing::Position3D>> aClippedPoly;
446 Clipping::clipPolygonAtRectangle( aPoly, pPosHelper->getScaledLogicClipDoubleRect(), aClippedPoly, false );
447 ShapeFactory::closePolygon(aClippedPoly); //again necessary after clipping
448 aPoly = std::move(aClippedPoly);
451 if(!ShapeFactory::hasPolygonAnyLines(aPoly))
452 return false;
454 //transformation 3) -> 4)
455 pPosHelper->transformScaledLogicToScene( aPoly );
457 //create area:
458 rtl::Reference< SvxShape > xShape;
459 if(m_nDimension==3)
461 xShape = ShapeFactory::createArea3D( xSeriesGroupShape_Shapes
462 , aPoly, getTransformedDepth() );
464 else //m_nDimension!=3
466 xShape = ShapeFactory::createArea2D( xSeriesGroupShape_Shapes
467 , aPoly );
469 PropertyMapper::setMappedProperties( *xShape
470 , pSeries->getPropertiesOfSeries()
471 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
472 //because of this name this line will be used for marking
473 ::chart::ShapeFactory::setShapeName(xShape, u"MarkHandles"_ustr);
474 return true;
477 void AreaChart::impl_createSeriesShapes()
479 //the polygon shapes for each series need to be created before
481 //iterate through all series again to create the series shapes
482 for( auto const& rZSlot : m_aZSlots )
484 for( auto const& rXSlot : rZSlot )
486 std::map< sal_Int32, std::vector<std::vector<css::drawing::Position3D>>* > aPreviousSeriesPolyMap;//a PreviousSeriesPoly for each different nAttachedAxisIndex
487 std::vector<std::vector<css::drawing::Position3D>>* pSeriesPoly = nullptr;
489 //iterate through all series
490 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
492 sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex();
493 PlottingPositionHelper& rPosHelper = getPlottingPositionHelper(nAttachedAxisIndex);
494 m_pPosHelper = &rPosHelper;
496 createRegressionCurvesShapes( *pSeries, m_xErrorBarTarget, m_xRegressionCurveEquationTarget,
497 m_pPosHelper->maySkipPointsInRegressionCalculation());
499 pSeriesPoly = &pSeries->m_aPolyPolygonShape3D;
500 if( m_bArea )
502 if (!impl_createArea(pSeries.get(), pSeriesPoly,
503 aPreviousSeriesPolyMap[nAttachedAxisIndex], &rPosHelper))
504 continue;
506 if( m_bLine )
508 if (!impl_createLine(pSeries.get(), pSeriesPoly, &rPosHelper))
509 continue;
511 aPreviousSeriesPolyMap[nAttachedAxisIndex] = pSeriesPoly;
512 }//next series in x slot (next y slot)
513 }//next x slot
514 }//next z slot
517 namespace
520 void lcl_reorderSeries( std::vector< std::vector< VDataSeriesGroup > >& rZSlots )
522 std::vector< std::vector< VDataSeriesGroup > > aRet;
523 aRet.reserve( rZSlots.size() );
525 std::vector< std::vector< VDataSeriesGroup > >::reverse_iterator aZIt( rZSlots.rbegin() );
526 std::vector< std::vector< VDataSeriesGroup > >::reverse_iterator aZEnd( rZSlots.rend() );
527 for( ; aZIt != aZEnd; ++aZIt )
529 std::vector< VDataSeriesGroup > aXSlot;
530 aXSlot.reserve( aZIt->size() );
532 std::vector< VDataSeriesGroup >::reverse_iterator aXIt( aZIt->rbegin() );
533 std::vector< VDataSeriesGroup >::reverse_iterator aXEnd( aZIt->rend() );
534 for( ; aXIt != aXEnd; ++aXIt )
535 aXSlot.push_back(std::move(*aXIt));
537 aRet.push_back(std::move(aXSlot));
540 rZSlots = std::move(aRet);
543 //better performance for big data
544 struct FormerPoint
546 FormerPoint( double fX, double fY, double fZ )
547 : m_fX(fX), m_fY(fY), m_fZ(fZ)
549 FormerPoint()
550 : m_fX(std::numeric_limits<double>::quiet_NaN())
551 , m_fY(std::numeric_limits<double>::quiet_NaN())
552 , m_fZ(std::numeric_limits<double>::quiet_NaN())
556 double m_fX;
557 double m_fY;
558 double m_fZ;
561 }//anonymous namespace
563 void AreaChart::createShapes()
565 if( m_aZSlots.empty() ) //no series
566 return;
568 //tdf#127813 Don't reverse the series in OOXML-heavy environments
569 if( officecfg::Office::Compatibility::View::ReverseSeriesOrderAreaAndNetChart::get() && m_nDimension == 2 && ( m_bArea || !m_bCategoryXAxis ) )
570 lcl_reorderSeries( m_aZSlots );
572 OSL_ENSURE(m_xLogicTarget.is()&&m_xFinalTarget.is(),"AreaChart is not proper initialized");
573 if(!(m_xLogicTarget.is()&&m_xFinalTarget.is()))
574 return;
576 //the text labels should be always on top of the other series shapes
577 //for area chart the error bars should be always on top of the other series shapes
579 //therefore create an own group for the texts and the error bars to move them to front
580 //(because the text group is created after the series group the texts are displayed on top)
581 m_xSeriesTarget = createGroupShape( m_xLogicTarget );
582 if( m_bArea )
583 m_xErrorBarTarget = createGroupShape( m_xLogicTarget );
584 else
585 m_xErrorBarTarget = m_xSeriesTarget;
586 m_xTextTarget = ShapeFactory::createGroup2D( m_xFinalTarget );
587 m_xRegressionCurveEquationTarget = ShapeFactory::createGroup2D( m_xFinalTarget );
589 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
591 //update/create information for current group
592 double fLogicZ = 1.0;//as defined
594 sal_Int32 nStartIndex = 0; // inclusive ;..todo get somehow from x scale
595 sal_Int32 nEndIndex = VSeriesPlotter::getPointCount();
596 if(nEndIndex<=0)
597 nEndIndex=1;
599 //better performance for big data
600 std::map< VDataSeries*, FormerPoint > aSeriesFormerPointMap;
601 m_bPointsWereSkipped = false;
602 sal_Int32 nSkippedPoints = 0;
603 sal_Int32 nCreatedPoints = 0;
605 bool bDateCategory = (m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis());
607 std::vector<std::map< sal_Int32, double > > aLogicYSumMapByX(nEndIndex);//one for each different nAttachedAxisIndex
608 for( auto const& rZSlot : m_aZSlots )
610 //iterate through all x slots in this category to get 100percent sum
611 for( auto const& rXSlot : rZSlot )
613 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
615 if(!pSeries)
616 continue;
618 if (bDateCategory)
619 pSeries->doSortByXValues();
621 for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; nIndex++ )
623 std::map< sal_Int32, double >& rLogicYSumMap = aLogicYSumMapByX[nIndex];
624 sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex();
625 rLogicYSumMap.insert({nAttachedAxisIndex, 0.0});
627 m_pPosHelper = &getPlottingPositionHelper(nAttachedAxisIndex);
629 double fAdd = pSeries->getYValue( nIndex );
630 if( !std::isnan(fAdd) && !std::isinf(fAdd) )
631 rLogicYSumMap[nAttachedAxisIndex] += fabs( fAdd );
637 const bool bUseErrorRectangle = officecfg::Office::Chart::ErrorProperties::ErrorRectangle::get();
639 sal_Int32 nZ=1;
640 for( auto const& rZSlot : m_aZSlots )
642 //for the area chart there should be at most one x slot (no side by side stacking available)
643 //attention different: xSlots are always interpreted as independent areas one behind the other: @todo this doesn't work why not???
644 for( auto const& rXSlot : rZSlot )
646 std::vector<std::map< sal_Int32, double > > aLogicYForNextSeriesMapByX(nEndIndex); //one for each different nAttachedAxisIndex
647 //iterate through all series
648 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
650 if(!pSeries)
651 continue;
653 rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShapeFrontChild(pSeries.get(), m_xSeriesTarget);
655 sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex();
656 double fXMin, fXMax;
657 pSeries->getMinMaxXValue(fXMin, fXMax);
658 PlottingPositionHelper& rPosHelper = getPlottingPositionHelper(nAttachedAxisIndex);
659 m_pPosHelper = &rPosHelper;
661 if(m_nDimension==3)
662 fLogicZ = nZ+0.5;
663 pSeries->m_fLogicZPos = fLogicZ;
665 for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; nIndex++ )
668 /* #i70133# ignore points outside of series length in standard area
669 charts. Stacked area charts will use missing points as zeros. In
670 standard charts, pSeriesList contains only one series. */
671 if( m_bArea && (rXSlot.m_aSeriesVector.size() == 1) && (nIndex >= pSeries->getTotalPointCount()) )
672 continue;
674 //collect data point information (logic coordinates, style ):
675 double fLogicX = pSeries->getXValue(nIndex);
676 if (bDateCategory)
678 if (std::isnan(fLogicX) || (fLogicX < fXMin || fLogicX > fXMax))
679 continue;
681 fLogicX = DateHelper::RasterizeDateValue( fLogicX, m_aNullDate, m_nTimeResolution );
683 double fLogicY = pSeries->getYValue(nIndex);
685 if( m_nDimension==3 && m_bArea && rXSlot.m_aSeriesVector.size()!=1 )
686 fLogicY = fabs( fLogicY );
688 double fLogicValueForLabeDisplay = fLogicY;
689 std::map< sal_Int32, double >& rLogicYSumMap = aLogicYSumMapByX[nIndex];
690 if (rPosHelper.isPercentY() && rLogicYSumMap[nAttachedAxisIndex] != 0.0)
692 fLogicY = fabs( fLogicY )/rLogicYSumMap[nAttachedAxisIndex];
695 if( std::isnan(fLogicX) || std::isinf(fLogicX)
696 || std::isnan(fLogicY) || std::isinf(fLogicY)
697 || std::isnan(fLogicZ) || std::isinf(fLogicZ) )
699 if( pSeries->getMissingValueTreatment() == css::chart::MissingValueTreatment::LEAVE_GAP )
701 std::vector<std::vector<css::drawing::Position3D>>& rPolygon = pSeries->m_aPolyPolygonShape3D;
702 sal_Int32& rIndex = pSeries->m_nPolygonIndex;
703 if( 0<= rIndex && o3tl::make_unsigned(rIndex) < rPolygon.size() )
705 if( !rPolygon[ rIndex ].empty() )
706 rIndex++; //start a new polygon for the next point if the current poly is not empty
709 continue;
712 std::map< sal_Int32, double >& rLogicYForNextSeriesMap = aLogicYForNextSeriesMapByX[nIndex];
713 rLogicYForNextSeriesMap.try_emplace(nAttachedAxisIndex, 0.0);
715 double fPreviousYValue = rLogicYForNextSeriesMap[nAttachedAxisIndex];
716 fLogicY += rLogicYForNextSeriesMap[nAttachedAxisIndex];
717 rLogicYForNextSeriesMap[nAttachedAxisIndex] = fLogicY;
719 bool bIsVisible = rPosHelper.isLogicVisible(fLogicX, fLogicY, fLogicZ);
721 //remind minimal and maximal x values for area 'grounding' points
722 //only for filled area
724 double& rfMinX = pSeries->m_fLogicMinX;
725 if(!nIndex||fLogicX<rfMinX)
726 rfMinX=fLogicX;
727 double& rfMaxX = pSeries->m_fLogicMaxX;
728 if(!nIndex||fLogicX>rfMaxX)
729 rfMaxX=fLogicX;
732 drawing::Position3D aUnscaledLogicPosition( fLogicX, fLogicY, fLogicZ );
733 drawing::Position3D aScaledLogicPosition(aUnscaledLogicPosition);
734 rPosHelper.doLogicScaling(aScaledLogicPosition);
736 //transformation 3) -> 4)
737 drawing::Position3D aScenePosition(
738 rPosHelper.transformLogicToScene(fLogicX, fLogicY, fLogicZ, false));
740 //better performance for big data
741 FormerPoint aFormerPoint( aSeriesFormerPointMap[pSeries.get()] );
742 rPosHelper.setCoordinateSystemResolution(m_aCoordinateSystemResolution);
743 if (!pSeries->isAttributedDataPoint(nIndex)
744 && rPosHelper.isSameForGivenResolution(
745 aFormerPoint.m_fX, aFormerPoint.m_fY, aFormerPoint.m_fZ,
746 aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY,
747 aScaledLogicPosition.PositionZ))
749 ++nSkippedPoints;
750 m_bPointsWereSkipped = true;
751 continue;
753 aSeriesFormerPointMap[pSeries.get()] = FormerPoint(aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY, aScaledLogicPosition.PositionZ);
755 //store point information for series polygon
756 //for area and/or line (symbols only do not need this)
757 if( isValidPosition(aScaledLogicPosition) )
759 AddPointToPoly( pSeries->m_aPolyPolygonShape3D, aScaledLogicPosition, pSeries->m_nPolygonIndex );
762 //create a single datapoint if point is visible
763 //apply clipping:
764 if( !bIsVisible )
765 continue;
767 bool bCreateYErrorBar = false, bCreateXErrorBar = false;
769 uno::Reference< beans::XPropertySet > xErrorBarProp(pSeries->getYErrorBarProperties(nIndex));
770 if( xErrorBarProp.is() )
772 bool bShowPositive = false;
773 bool bShowNegative = false;
774 xErrorBarProp->getPropertyValue(u"ShowPositiveError"_ustr) >>= bShowPositive;
775 xErrorBarProp->getPropertyValue(u"ShowNegativeError"_ustr) >>= bShowNegative;
776 bCreateYErrorBar = bShowPositive || bShowNegative;
779 xErrorBarProp = pSeries->getXErrorBarProperties(nIndex);
780 if ( xErrorBarProp.is() )
782 bool bShowPositive = false;
783 bool bShowNegative = false;
784 xErrorBarProp->getPropertyValue(u"ShowPositiveError"_ustr) >>= bShowPositive;
785 xErrorBarProp->getPropertyValue(u"ShowNegativeError"_ustr) >>= bShowNegative;
786 bCreateXErrorBar = bShowPositive || bShowNegative;
790 Symbol* pSymbolProperties = m_bSymbol ? pSeries->getSymbolProperties( nIndex ) : nullptr;
791 bool bCreateSymbol = pSymbolProperties && (pSymbolProperties->Style != SymbolStyle_NONE);
793 if( !bCreateSymbol && !bCreateYErrorBar &&
794 !bCreateXErrorBar && !pSeries->getDataPointLabelIfLabel(nIndex) )
795 continue;
798 nCreatedPoints++;
800 //create data point
801 drawing::Direction3D aSymbolSize(0,0,0);
802 if( bCreateSymbol )
804 if(m_nDimension!=3)
806 //create a group shape for this point and add to the series shape:
807 OUString aPointCID = ObjectIdentifier::createPointCID(
808 pSeries->getPointCID_Stub(), nIndex );
809 rtl::Reference<SvxShapeGroupAnyD> xPointGroupShape_Shapes;
810 if (pSymbolProperties->Style == SymbolStyle_STANDARD || pSymbolProperties->Style == SymbolStyle_GRAPHIC)
811 xPointGroupShape_Shapes = createGroupShape(xSeriesGroupShape_Shapes,aPointCID);
813 if (pSymbolProperties->Style != SymbolStyle_NONE)
815 aSymbolSize.DirectionX = pSymbolProperties->Size.Width;
816 aSymbolSize.DirectionY = pSymbolProperties->Size.Height;
819 if (pSymbolProperties->Style == SymbolStyle_STANDARD)
821 sal_Int32 nSymbol = pSymbolProperties->StandardSymbol;
822 ShapeFactory::createSymbol2D(
823 xPointGroupShape_Shapes, aScenePosition, aSymbolSize,
824 nSymbol, pSymbolProperties->BorderColor,
825 pSymbolProperties->FillColor);
827 else if (pSymbolProperties->Style == SymbolStyle_GRAPHIC)
829 ShapeFactory::createGraphic2D(xPointGroupShape_Shapes,
830 aScenePosition, aSymbolSize,
831 pSymbolProperties->Graphic);
833 //@todo other symbol styles
836 //create error bars or rectangles, depending on configuration
837 if ( bUseErrorRectangle )
839 if ( bCreateXErrorBar || bCreateYErrorBar )
841 createErrorRectangle(
842 aUnscaledLogicPosition,
843 *pSeries,
844 nIndex,
845 m_xErrorBarTarget,
846 bCreateXErrorBar,
847 bCreateYErrorBar );
850 else
852 if (bCreateXErrorBar)
853 createErrorBar_X( aUnscaledLogicPosition, *pSeries, nIndex, m_xErrorBarTarget );
855 if (bCreateYErrorBar)
856 createErrorBar_Y( aUnscaledLogicPosition, *pSeries, nIndex, m_xErrorBarTarget, nullptr );
859 //create data point label
860 if( pSeries->getDataPointLabelIfLabel(nIndex) )
862 LabelAlignment eAlignment = LABEL_ALIGN_TOP;
863 sal_Int32 nLabelPlacement = pSeries->getLabelPlacement(
864 nIndex, m_xChartTypeModel, rPosHelper.isSwapXAndY());
866 if (m_bArea && nLabelPlacement == css::chart::DataLabelPlacement::CENTER)
868 if (fPreviousYValue)
869 fLogicY -= (fLogicY - fPreviousYValue) / 2.0;
870 else
871 fLogicY = (fLogicY + rPosHelper.getLogicMinY()) / 2.0;
872 aScenePosition = rPosHelper.transformLogicToScene(fLogicX, fLogicY, fLogicZ, false);
875 drawing::Position3D aScenePosition3D( aScenePosition.PositionX
876 , aScenePosition.PositionY
877 , aScenePosition.PositionZ+getTransformedDepth() );
879 switch(nLabelPlacement)
881 case css::chart::DataLabelPlacement::TOP:
882 aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1);
883 eAlignment = LABEL_ALIGN_TOP;
884 break;
885 case css::chart::DataLabelPlacement::BOTTOM:
886 aScenePosition3D.PositionY += (aSymbolSize.DirectionY/2+1);
887 eAlignment = LABEL_ALIGN_BOTTOM;
888 break;
889 case css::chart::DataLabelPlacement::LEFT:
890 aScenePosition3D.PositionX -= (aSymbolSize.DirectionX/2+1);
891 eAlignment = LABEL_ALIGN_LEFT;
892 break;
893 case css::chart::DataLabelPlacement::RIGHT:
894 aScenePosition3D.PositionX += (aSymbolSize.DirectionX/2+1);
895 eAlignment = LABEL_ALIGN_RIGHT;
896 break;
897 case css::chart::DataLabelPlacement::CENTER:
898 eAlignment = LABEL_ALIGN_CENTER;
899 break;
900 default:
901 OSL_FAIL("this label alignment is not implemented yet");
902 aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1);
903 eAlignment = LABEL_ALIGN_TOP;
904 break;
907 awt::Point aScreenPosition2D;//get the screen position for the labels
908 sal_Int32 nOffset = 100; //todo maybe calculate this font height dependent
910 if(eAlignment==LABEL_ALIGN_CENTER || m_nDimension == 3 )
911 nOffset = 0;
912 aScreenPosition2D = LabelPositionHelper(m_nDimension,m_xLogicTarget)
913 .transformSceneToScreenPosition( aScenePosition3D );
916 createDataLabel( m_xTextTarget, *pSeries, nIndex
917 , fLogicValueForLabeDisplay
918 , rLogicYSumMap[nAttachedAxisIndex], aScreenPosition2D, eAlignment, nOffset );
923 }//next series in x slot (next y slot)
924 }//next x slot
925 ++nZ;
926 }//next z slot
928 impl_createSeriesShapes();
930 /* @todo remove series shapes if empty
931 //remove and delete point-group-shape if empty
932 if(!xSeriesGroupShape_Shapes->getCount())
934 pSeries->m_xShape.set(NULL);
935 m_xLogicTarget->remove(xSeriesGroupShape_Shape);
939 //remove and delete series-group-shape if empty
941 //... todo
943 SAL_INFO(
944 "chart2",
945 "skipped points: " << nSkippedPoints << " created points: "
946 << nCreatedPoints);
949 } //namespace chart
951 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */