Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / chart2 / source / view / charttypes / AreaChart.cxx
blobe9c54add6c0f03f2998e076f25331f01d19c6598
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 <ChartTypeHelper.hxx>
28 #include <LabelPositionHelper.hxx>
29 #include <Clipping.hxx>
30 #include <Stripe.hxx>
31 #include <DateHelper.hxx>
32 #include <unonames.hxx>
33 #include <ConfigAccess.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 <rtl/math.hxx>
40 #include <sal/log.hxx>
41 #include <osl/diagnose.h>
43 #include <com/sun/star/drawing/DoubleSequence.hpp>
44 #include <com/sun/star/drawing/XShapes.hpp>
45 #include <com/sun/star/beans/XPropertySet.hpp>
47 namespace chart
49 using namespace ::com::sun::star;
50 using namespace ::rtl::math;
51 using namespace ::com::sun::star::chart2;
53 AreaChart::AreaChart( const uno::Reference<XChartType>& xChartTypeModel
54 , sal_Int32 nDimensionCount
55 , bool bCategoryXAxis
56 , bool bNoArea
58 : VSeriesPlotter( xChartTypeModel, nDimensionCount, bCategoryXAxis )
59 , m_pMainPosHelper(new PlottingPositionHelper())
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 m_pMainPosHelper->AllowShiftXAxisPos(true);
68 m_pMainPosHelper->AllowShiftZAxisPos(true);
70 PlotterBase::m_pPosHelper = m_pMainPosHelper.get();
71 VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper.get();
73 try
75 if( m_xChartTypeModelProps.is() )
77 m_xChartTypeModelProps->getPropertyValue(CHART_UNONAME_CURVE_STYLE) >>= m_eCurveStyle;
78 m_xChartTypeModelProps->getPropertyValue(CHART_UNONAME_CURVE_RESOLUTION) >>= m_nCurveResolution;
79 m_xChartTypeModelProps->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( drawing::PolyPolygonShape3D& rPolyPoly, PlottingPositionHelper& rPosHelper )
158 sal_Int32 nPolyCount = rPolyPoly.SequenceX.getLength();
159 if(!nPolyCount)
160 return;
162 drawing::PolyPolygonShape3D aTmp;
163 aTmp.SequenceX.realloc(nPolyCount);
164 aTmp.SequenceY.realloc(nPolyCount);
165 aTmp.SequenceZ.realloc(nPolyCount);
167 for( sal_Int32 nPolygonIndex = 0; nPolygonIndex<nPolyCount; nPolygonIndex++ )
169 drawing::DoubleSequence* pOuterSourceX = &rPolyPoly.SequenceX.getArray()[nPolygonIndex];
170 drawing::DoubleSequence* pOuterSourceY = &rPolyPoly.SequenceY.getArray()[nPolygonIndex];
171 drawing::DoubleSequence* pOuterSourceZ = &rPolyPoly.SequenceZ.getArray()[nPolygonIndex];
173 drawing::DoubleSequence* pOuterTargetX = &aTmp.SequenceX.getArray()[nPolygonIndex];
174 drawing::DoubleSequence* pOuterTargetY = &aTmp.SequenceY.getArray()[nPolygonIndex];
175 drawing::DoubleSequence* pOuterTargetZ = &aTmp.SequenceZ.getArray()[nPolygonIndex];
177 sal_Int32 nPointCount = pOuterSourceX->getLength();
178 if( !nPointCount )
179 continue;
181 pOuterTargetX->realloc(nPointCount);
182 pOuterTargetY->realloc(nPointCount);
183 pOuterTargetZ->realloc(nPointCount);
185 double* pSourceX = pOuterSourceX->getArray();
186 double* pSourceY = pOuterSourceY->getArray();
187 double* pSourceZ = pOuterSourceZ->getArray();
189 double* pTargetX = pOuterTargetX->getArray();
190 double* pTargetY = pOuterTargetY->getArray();
191 double* pTargetZ = pOuterTargetZ->getArray();
193 //copy first point
194 *pTargetX=*pSourceX++;
195 *pTargetY=*pSourceY++;
196 *pTargetZ=*pSourceZ++;
197 sal_Int32 nTargetPointCount=1;
199 for( sal_Int32 nSource=1; nSource<nPointCount; nSource++ )
201 if( !rPosHelper.isSameForGivenResolution( *pTargetX, *pTargetY, *pTargetZ
202 , *pSourceX, *pSourceY, *pSourceZ ) )
204 pTargetX++; pTargetY++; pTargetZ++;
205 *pTargetX=*pSourceX;
206 *pTargetY=*pSourceY;
207 *pTargetZ=*pSourceZ;
208 nTargetPointCount++;
210 pSourceX++; pSourceY++; pSourceZ++;
213 //free unused space
214 if( nTargetPointCount<nPointCount )
216 pOuterTargetX->realloc(nTargetPointCount);
217 pOuterTargetY->realloc(nTargetPointCount);
218 pOuterTargetZ->realloc(nTargetPointCount);
221 pOuterSourceX->realloc(0);
222 pOuterSourceY->realloc(0);
223 pOuterSourceZ->realloc(0);
226 //free space
227 rPolyPoly.SequenceX.realloc(nPolyCount);
228 rPolyPoly.SequenceY.realloc(nPolyCount);
229 rPolyPoly.SequenceZ.realloc(nPolyCount);
231 rPolyPoly=aTmp;
234 bool AreaChart::create_stepped_line( drawing::PolyPolygonShape3D aStartPoly, chart2::CurveStyle eCurveStyle, PlottingPositionHelper const * pPosHelper, drawing::PolyPolygonShape3D &aPoly )
236 sal_uInt32 nOuterCount = aStartPoly.SequenceX.getLength();
237 if ( !nOuterCount )
238 return false;
240 drawing::PolyPolygonShape3D aSteppedPoly;
241 aSteppedPoly.SequenceX.realloc(nOuterCount);
242 aSteppedPoly.SequenceY.realloc(nOuterCount);
243 aSteppedPoly.SequenceZ.realloc(nOuterCount);
245 for( sal_uInt32 nOuter = 0; nOuter < nOuterCount; ++nOuter )
247 if( aStartPoly.SequenceX[nOuter].getLength() <= 1 )
248 continue; //we need at least two points
250 sal_uInt32 nMaxIndexPoints = aStartPoly.SequenceX[nOuter].getLength()-1; // is >1
251 sal_uInt32 nNewIndexPoints = 0;
252 if ( eCurveStyle==CurveStyle_STEP_START || eCurveStyle==CurveStyle_STEP_END)
253 nNewIndexPoints = nMaxIndexPoints * 2 + 1;
254 else
255 nNewIndexPoints = nMaxIndexPoints * 3 + 1;
257 const double* pOldX = aStartPoly.SequenceX[nOuter].getConstArray();
258 const double* pOldY = aStartPoly.SequenceY[nOuter].getConstArray();
259 const double* pOldZ = aStartPoly.SequenceZ[nOuter].getConstArray();
261 aSteppedPoly.SequenceX[nOuter].realloc( nNewIndexPoints );
262 aSteppedPoly.SequenceY[nOuter].realloc( nNewIndexPoints );
263 aSteppedPoly.SequenceZ[nOuter].realloc( nNewIndexPoints );
265 double* pNewX = aSteppedPoly.SequenceX[nOuter].getArray();
266 double* pNewY = aSteppedPoly.SequenceY[nOuter].getArray();
267 double* pNewZ = aSteppedPoly.SequenceZ[nOuter].getArray();
269 pNewX[0] = pOldX[0];
270 pNewY[0] = pOldY[0];
271 pNewZ[0] = pOldZ[0];
272 for( sal_uInt32 oi = 0; oi < nMaxIndexPoints; oi++ )
274 switch ( eCurveStyle )
276 case CurveStyle_STEP_START:
277 /** O
281 O-----+
283 // create the intermediate point
284 pNewX[1+oi*2] = pOldX[oi+1];
285 pNewY[1+oi*2] = pOldY[oi];
286 pNewZ[1+oi*2] = pOldZ[oi];
287 // and now the normal one
288 pNewX[1+oi*2+1] = pOldX[oi+1];
289 pNewY[1+oi*2+1] = pOldY[oi+1];
290 pNewZ[1+oi*2+1] = pOldZ[oi+1];
291 break;
292 case CurveStyle_STEP_END:
293 /** +------O
299 // create the intermediate point
300 pNewX[1+oi*2] = pOldX[oi];
301 pNewY[1+oi*2] = pOldY[oi+1];
302 pNewZ[1+oi*2] = pOldZ[oi];
303 // and now the normal one
304 pNewX[1+oi*2+1] = pOldX[oi+1];
305 pNewY[1+oi*2+1] = pOldY[oi+1];
306 pNewZ[1+oi*2+1] = pOldZ[oi+1];
307 break;
308 case CurveStyle_STEP_CENTER_X:
309 /** +--O
313 O--+
315 // create the first intermediate point
316 pNewX[1+oi*3] = (pOldX[oi]+pOldX[oi+1])/2;
317 pNewY[1+oi*3] = pOldY[oi];
318 pNewZ[1+oi*3] = pOldZ[oi];
319 // create the second intermediate point
320 pNewX[1+oi*3+1] = (pOldX[oi]+pOldX[oi+1])/2;
321 pNewY[1+oi*3+1] = pOldY[oi+1];
322 pNewZ[1+oi*3+1] = pOldZ[oi];
323 // and now the normal one
324 pNewX[1+oi*3+2] = pOldX[oi+1];
325 pNewY[1+oi*3+2] = pOldY[oi+1];
326 pNewZ[1+oi*3+2] = pOldZ[oi+1];
327 break;
328 case CurveStyle_STEP_CENTER_Y:
329 /** O
331 +-----+
335 // create the first intermediate point
336 pNewX[1+oi*3] = pOldX[oi];
337 pNewY[1+oi*3] = (pOldY[oi]+pOldY[oi+1])/2;
338 pNewZ[1+oi*3] = pOldZ[oi];
339 // create the second intermediate point
340 pNewX[1+oi*3+1] = pOldX[oi+1];
341 pNewY[1+oi*3+1] = (pOldY[oi]+pOldY[oi+1])/2;
342 pNewZ[1+oi*3+1] = pOldZ[oi];
343 // and now the normal one
344 pNewX[1+oi*3+2] = pOldX[oi+1];
345 pNewY[1+oi*3+2] = pOldY[oi+1];
346 pNewZ[1+oi*3+2] = pOldZ[oi+1];
347 break;
348 default:
349 // this should never be executed
350 OSL_FAIL("Unknown curvestyle in AreaChart::create_stepped_line");
354 Clipping::clipPolygonAtRectangle( aSteppedPoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
356 return true;
359 bool AreaChart::impl_createLine( VDataSeries* pSeries
360 , drawing::PolyPolygonShape3D const * pSeriesPoly
361 , PlottingPositionHelper* pPosHelper )
363 //return true if a line was created successfully
364 uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShapeBackChild(pSeries, m_xSeriesTarget);
366 drawing::PolyPolygonShape3D aPoly;
367 if(m_eCurveStyle==CurveStyle_CUBIC_SPLINES)
369 drawing::PolyPolygonShape3D aSplinePoly;
370 SplineCalculater::CalculateCubicSplines( *pSeriesPoly, aSplinePoly, m_nCurveResolution );
371 lcl_removeDuplicatePoints( aSplinePoly, *pPosHelper );
372 Clipping::clipPolygonAtRectangle( aSplinePoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
374 else if(m_eCurveStyle==CurveStyle_B_SPLINES)
376 drawing::PolyPolygonShape3D aSplinePoly;
377 SplineCalculater::CalculateBSplines( *pSeriesPoly, aSplinePoly, m_nCurveResolution, m_nSplineOrder );
378 lcl_removeDuplicatePoints( aSplinePoly, *pPosHelper );
379 Clipping::clipPolygonAtRectangle( aSplinePoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
381 else if (m_eCurveStyle==CurveStyle_STEP_START ||
382 m_eCurveStyle==CurveStyle_STEP_END ||
383 m_eCurveStyle==CurveStyle_STEP_CENTER_Y ||
384 m_eCurveStyle==CurveStyle_STEP_CENTER_X
387 if (!create_stepped_line(*pSeriesPoly, m_eCurveStyle, pPosHelper, aPoly))
389 return false;
392 else
393 { // default to creating a straight line
394 SAL_WARN_IF(m_eCurveStyle != CurveStyle_LINES, "chart2.areachart", "Unknown curve style");
395 Clipping::clipPolygonAtRectangle( *pSeriesPoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
398 if(!ShapeFactory::hasPolygonAnyLines(aPoly))
399 return false;
401 //transformation 3) -> 4)
402 pPosHelper->transformScaledLogicToScene( aPoly );
404 //create line:
405 uno::Reference< drawing::XShape > xShape;
406 if(m_nDimension==3)
408 double fDepth = getTransformedDepth();
409 sal_Int32 nPolyCount = aPoly.SequenceX.getLength();
410 for(sal_Int32 nPoly=0;nPoly<nPolyCount;nPoly++)
412 sal_Int32 nPointCount = aPoly.SequenceX[nPoly].getLength();
413 for(sal_Int32 nPoint=0;nPoint<nPointCount-1;nPoint++)
415 drawing::Position3D aPoint1, aPoint2;
416 aPoint1.PositionX = aPoly.SequenceX[nPoly][nPoint+1];
417 aPoint1.PositionY = aPoly.SequenceY[nPoly][nPoint+1];
418 aPoint1.PositionZ = aPoly.SequenceZ[nPoly][nPoint+1];
420 aPoint2.PositionX = aPoly.SequenceX[nPoly][nPoint];
421 aPoint2.PositionY = aPoly.SequenceY[nPoly][nPoint];
422 aPoint2.PositionZ = aPoly.SequenceZ[nPoly][nPoint];
424 m_pShapeFactory->createStripe(xSeriesGroupShape_Shapes
425 , Stripe( aPoint1, aPoint2, fDepth )
426 , pSeries->getPropertiesOfSeries(), PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), true, 1 );
430 else //m_nDimension!=3
432 xShape = m_pShapeFactory->createLine2D( xSeriesGroupShape_Shapes
433 , PolyToPointSequence( aPoly ) );
434 setMappedProperties( xShape
435 , pSeries->getPropertiesOfSeries()
436 , PropertyMapper::getPropertyNameMapForLineSeriesProperties() );
437 //because of this name this line will be used for marking
438 ::chart::ShapeFactory::setShapeName(xShape, "MarkHandles");
440 return true;
443 bool AreaChart::impl_createArea( VDataSeries* pSeries
444 , drawing::PolyPolygonShape3D const * pSeriesPoly
445 , drawing::PolyPolygonShape3D const * pPreviousSeriesPoly
446 , PlottingPositionHelper const * pPosHelper )
448 //return true if an area was created successfully
450 uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShapeBackChild(pSeries, m_xSeriesTarget);
451 double zValue = pSeries->m_fLogicZPos;
453 drawing::PolyPolygonShape3D aPoly( *pSeriesPoly );
454 //add second part to the polygon (grounding points or previous series points)
455 if(!pPreviousSeriesPoly)
457 double fMinX = pSeries->m_fLogicMinX;
458 double fMaxX = pSeries->m_fLogicMaxX;
459 double fY = pPosHelper->getBaseValueY();//logic grounding
460 if( m_nDimension==3 )
461 fY = pPosHelper->getLogicMinY();
463 //clip to scale
464 if(fMaxX<pPosHelper->getLogicMinX() || fMinX>pPosHelper->getLogicMaxX())
465 return false;//no visible shape needed
466 pPosHelper->clipLogicValues( &fMinX, &fY, nullptr );
467 pPosHelper->clipLogicValues( &fMaxX, nullptr, nullptr );
469 //apply scaling
471 pPosHelper->doLogicScaling( &fMinX, &fY, &zValue );
472 pPosHelper->doLogicScaling( &fMaxX, nullptr, nullptr );
475 AddPointToPoly( aPoly, drawing::Position3D( fMaxX,fY,zValue) );
476 AddPointToPoly( aPoly, drawing::Position3D( fMinX,fY,zValue) );
478 else
480 appendPoly( aPoly, *pPreviousSeriesPoly );
482 ShapeFactory::closePolygon(aPoly);
484 //apply clipping
486 drawing::PolyPolygonShape3D aClippedPoly;
487 Clipping::clipPolygonAtRectangle( aPoly, pPosHelper->getScaledLogicClipDoubleRect(), aClippedPoly, false );
488 ShapeFactory::closePolygon(aClippedPoly); //again necessary after clipping
489 aPoly = aClippedPoly;
492 if(!ShapeFactory::hasPolygonAnyLines(aPoly))
493 return false;
495 //transformation 3) -> 4)
496 pPosHelper->transformScaledLogicToScene( aPoly );
498 //create area:
499 uno::Reference< drawing::XShape > xShape;
500 if(m_nDimension==3)
502 xShape = m_pShapeFactory->createArea3D( xSeriesGroupShape_Shapes
503 , aPoly, getTransformedDepth() );
505 else //m_nDimension!=3
507 xShape = m_pShapeFactory->createArea2D( xSeriesGroupShape_Shapes
508 , aPoly );
510 setMappedProperties( xShape
511 , pSeries->getPropertiesOfSeries()
512 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
513 //because of this name this line will be used for marking
514 ::chart::ShapeFactory::setShapeName(xShape, "MarkHandles");
515 return true;
518 void AreaChart::impl_createSeriesShapes()
520 //the polygon shapes for each series need to be created before
522 //iterate through all series again to create the series shapes
523 for( auto const& rZSlot : m_aZSlots )
525 for( auto const& rXSlot : rZSlot )
527 std::map< sal_Int32, drawing::PolyPolygonShape3D* > aPreviousSeriesPolyMap;//a PreviousSeriesPoly for each different nAttachedAxisIndex
528 drawing::PolyPolygonShape3D* pSeriesPoly = nullptr;
530 //iterate through all series
531 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
533 sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex();
534 PlottingPositionHelper& rPosHelper = getPlottingPositionHelper(nAttachedAxisIndex);
535 m_pPosHelper = &rPosHelper;
537 createRegressionCurvesShapes( *pSeries, m_xErrorBarTarget, m_xRegressionCurveEquationTarget,
538 m_pPosHelper->maySkipPointsInRegressionCalculation());
540 pSeriesPoly = &pSeries->m_aPolyPolygonShape3D;
541 if( m_bArea )
543 if (!impl_createArea(pSeries.get(), pSeriesPoly,
544 aPreviousSeriesPolyMap[nAttachedAxisIndex], &rPosHelper))
545 continue;
547 if( m_bLine )
549 if (!impl_createLine(pSeries.get(), pSeriesPoly, &rPosHelper))
550 continue;
552 aPreviousSeriesPolyMap[nAttachedAxisIndex] = pSeriesPoly;
553 }//next series in x slot (next y slot)
554 }//next x slot
555 }//next z slot
558 namespace
561 void lcl_reorderSeries( std::vector< std::vector< VDataSeriesGroup > >& rZSlots )
563 std::vector< std::vector< VDataSeriesGroup > > aRet;
564 aRet.reserve( rZSlots.size() );
566 std::vector< std::vector< VDataSeriesGroup > >::reverse_iterator aZIt( rZSlots.rbegin() );
567 std::vector< std::vector< VDataSeriesGroup > >::reverse_iterator aZEnd( rZSlots.rend() );
568 for( ; aZIt != aZEnd; ++aZIt )
570 std::vector< VDataSeriesGroup > aXSlot;
571 aXSlot.reserve( aZIt->size() );
573 std::vector< VDataSeriesGroup >::reverse_iterator aXIt( aZIt->rbegin() );
574 std::vector< VDataSeriesGroup >::reverse_iterator aXEnd( aZIt->rend() );
575 for( ; aXIt != aXEnd; ++aXIt )
576 aXSlot.push_back(std::move(*aXIt));
578 aRet.push_back(std::move(aXSlot));
581 rZSlots = std::move(aRet);
584 }//anonymous namespace
586 //better performance for big data
587 struct FormerPoint
589 FormerPoint( double fX, double fY, double fZ )
590 : m_fX(fX), m_fY(fY), m_fZ(fZ)
592 FormerPoint()
594 ::rtl::math::setNan( &m_fX );
595 ::rtl::math::setNan( &m_fY );
596 ::rtl::math::setNan( &m_fZ );
599 double m_fX;
600 double m_fY;
601 double m_fZ;
604 void AreaChart::createShapes()
606 if( m_aZSlots.empty() ) //no series
607 return;
609 if( m_nDimension == 2 && ( m_bArea || !m_bCategoryXAxis ) )
610 lcl_reorderSeries( m_aZSlots );
612 OSL_ENSURE(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"AreaChart is not proper initialized");
613 if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is()))
614 return;
616 //the text labels should be always on top of the other series shapes
617 //for area chart the error bars should be always on top of the other series shapes
619 //therefore create an own group for the texts and the error bars to move them to front
620 //(because the text group is created after the series group the texts are displayed on top)
621 m_xSeriesTarget = createGroupShape( m_xLogicTarget );
622 if( m_bArea )
623 m_xErrorBarTarget = createGroupShape( m_xLogicTarget );
624 else
625 m_xErrorBarTarget = m_xSeriesTarget;
626 m_xTextTarget = m_pShapeFactory->createGroup2D( m_xFinalTarget );
627 m_xRegressionCurveEquationTarget = m_pShapeFactory->createGroup2D( m_xFinalTarget );
629 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
631 //update/create information for current group
632 double fLogicZ = 1.0;//as defined
634 sal_Int32 nStartIndex = 0; // inclusive ;..todo get somehow from x scale
635 sal_Int32 nEndIndex = VSeriesPlotter::getPointCount();
636 if(nEndIndex<=0)
637 nEndIndex=1;
639 //better performance for big data
640 std::map< VDataSeries*, FormerPoint > aSeriesFormerPointMap;
641 m_bPointsWereSkipped = false;
642 sal_Int32 nSkippedPoints = 0;
643 sal_Int32 nCreatedPoints = 0;
645 bool bDateCategory = (m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis());
647 std::vector<std::map< sal_Int32, double > > aLogicYSumMapByX(nEndIndex);//one for each different nAttachedAxisIndex
648 for( auto const& rZSlot : m_aZSlots )
650 //iterate through all x slots in this category to get 100percent sum
651 for( auto const& rXSlot : rZSlot )
653 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
655 if(!pSeries)
656 continue;
658 if (bDateCategory)
659 pSeries->doSortByXValues();
661 for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; nIndex++ )
663 std::map< sal_Int32, double >& rLogicYSumMap = aLogicYSumMapByX[nIndex];
664 sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex();
665 if( rLogicYSumMap.find(nAttachedAxisIndex)==rLogicYSumMap.end() )
666 rLogicYSumMap[nAttachedAxisIndex]=0.0;
668 m_pPosHelper = &getPlottingPositionHelper(nAttachedAxisIndex);
670 double fAdd = pSeries->getYValue( nIndex );
671 if( !::rtl::math::isNan(fAdd) && !::rtl::math::isInf(fAdd) )
672 rLogicYSumMap[nAttachedAxisIndex] += fabs( fAdd );
678 sal_Int32 nZ=1;
679 for( auto const& rZSlot : m_aZSlots )
681 //for the area chart there should be at most one x slot (no side by side stacking available)
682 //attention different: xSlots are always interpreted as independent areas one behind the other: @todo this doesn't work why not???
683 for( auto const& rXSlot : rZSlot )
685 std::vector<std::map< sal_Int32, double > > aLogicYForNextSeriesMapByX(nEndIndex); //one for each different nAttachedAxisIndex
686 //iterate through all series
687 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
689 if(!pSeries)
690 continue;
692 uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShapeFrontChild(pSeries.get(), m_xSeriesTarget);
694 sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex();
695 PlottingPositionHelper& rPosHelper = getPlottingPositionHelper(nAttachedAxisIndex);
696 m_pPosHelper = &rPosHelper;
698 if(m_nDimension==3)
699 fLogicZ = nZ+0.5;
700 pSeries->m_fLogicZPos = fLogicZ;
702 for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; nIndex++ )
705 /* #i70133# ignore points outside of series length in standard area
706 charts. Stacked area charts will use missing points as zeros. In
707 standard charts, pSeriesList contains only one series. */
708 if( m_bArea && (rXSlot.m_aSeriesVector.size() == 1) && (nIndex >= pSeries->getTotalPointCount()) )
709 continue;
711 //collect data point information (logic coordinates, style ):
712 double fLogicX = pSeries->getXValue(nIndex);
713 if (bDateCategory)
715 if (rtl::math::isNan(fLogicX))
716 continue;
718 fLogicX = DateHelper::RasterizeDateValue( fLogicX, m_aNullDate, m_nTimeResolution );
720 double fLogicY = pSeries->getYValue(nIndex);
722 if( m_nDimension==3 && m_bArea && rXSlot.m_aSeriesVector.size()!=1 )
723 fLogicY = fabs( fLogicY );
725 std::map< sal_Int32, double >& rLogicYSumMap = aLogicYSumMapByX[nIndex];
726 if (rPosHelper.isPercentY() && rLogicYSumMap[nAttachedAxisIndex] != 0.0)
728 fLogicY = fabs( fLogicY )/rLogicYSumMap[nAttachedAxisIndex];
731 if( ::rtl::math::isNan(fLogicX) || ::rtl::math::isInf(fLogicX)
732 || ::rtl::math::isNan(fLogicY) || ::rtl::math::isInf(fLogicY)
733 || ::rtl::math::isNan(fLogicZ) || ::rtl::math::isInf(fLogicZ) )
735 if( pSeries->getMissingValueTreatment() == css::chart::MissingValueTreatment::LEAVE_GAP )
737 drawing::PolyPolygonShape3D& rPolygon = pSeries->m_aPolyPolygonShape3D;
738 sal_Int32& rIndex = pSeries->m_nPolygonIndex;
739 if( 0<= rIndex && rIndex < rPolygon.SequenceX.getLength() )
741 if( rPolygon.SequenceX[ rIndex ].hasElements() )
742 rIndex++; //start a new polygon for the next point if the current poly is not empty
745 continue;
748 std::map< sal_Int32, double >& rLogicYForNextSeriesMap = aLogicYForNextSeriesMapByX[nIndex];
749 if( rLogicYForNextSeriesMap.find(nAttachedAxisIndex) == rLogicYForNextSeriesMap.end() )
750 rLogicYForNextSeriesMap[nAttachedAxisIndex] = 0.0;
752 double fLogicValueForLabeDisplay = fLogicY;
754 fLogicY += rLogicYForNextSeriesMap[nAttachedAxisIndex];
755 rLogicYForNextSeriesMap[nAttachedAxisIndex] = fLogicY;
757 bool bIsVisible = rPosHelper.isLogicVisible(fLogicX, fLogicY, fLogicZ);
759 //remind minimal and maximal x values for area 'grounding' points
760 //only for filled area
762 double& rfMinX = pSeries->m_fLogicMinX;
763 if(!nIndex||fLogicX<rfMinX)
764 rfMinX=fLogicX;
765 double& rfMaxX = pSeries->m_fLogicMaxX;
766 if(!nIndex||fLogicX>rfMaxX)
767 rfMaxX=fLogicX;
770 drawing::Position3D aUnscaledLogicPosition( fLogicX, fLogicY, fLogicZ );
771 drawing::Position3D aScaledLogicPosition(aUnscaledLogicPosition);
772 rPosHelper.doLogicScaling(aScaledLogicPosition);
774 //transformation 3) -> 4)
775 drawing::Position3D aScenePosition(
776 rPosHelper.transformLogicToScene(fLogicX, fLogicY, fLogicZ, false));
778 //better performance for big data
779 FormerPoint aFormerPoint( aSeriesFormerPointMap[pSeries.get()] );
780 rPosHelper.setCoordinateSystemResolution(m_aCoordinateSystemResolution);
781 if (!pSeries->isAttributedDataPoint(nIndex)
782 && rPosHelper.isSameForGivenResolution(
783 aFormerPoint.m_fX, aFormerPoint.m_fY, aFormerPoint.m_fZ,
784 aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY,
785 aScaledLogicPosition.PositionZ))
787 ++nSkippedPoints;
788 m_bPointsWereSkipped = true;
789 continue;
791 aSeriesFormerPointMap[pSeries.get()] = FormerPoint(aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY, aScaledLogicPosition.PositionZ);
793 //store point information for series polygon
794 //for area and/or line (symbols only do not need this)
795 if( isValidPosition(aScaledLogicPosition) )
797 AddPointToPoly( pSeries->m_aPolyPolygonShape3D, aScaledLogicPosition, pSeries->m_nPolygonIndex );
800 //create a single datapoint if point is visible
801 //apply clipping:
802 if( !bIsVisible )
803 continue;
805 bool bCreateYErrorBar = false, bCreateXErrorBar = false;
807 uno::Reference< beans::XPropertySet > xErrorBarProp(pSeries->getYErrorBarProperties(nIndex));
808 if( xErrorBarProp.is() )
810 bool bShowPositive = false;
811 bool bShowNegative = false;
812 xErrorBarProp->getPropertyValue("ShowPositiveError") >>= bShowPositive;
813 xErrorBarProp->getPropertyValue("ShowNegativeError") >>= bShowNegative;
814 bCreateYErrorBar = bShowPositive || bShowNegative;
817 xErrorBarProp = pSeries->getXErrorBarProperties(nIndex);
818 if ( xErrorBarProp.is() )
820 bool bShowPositive = false;
821 bool bShowNegative = false;
822 xErrorBarProp->getPropertyValue("ShowPositiveError") >>= bShowPositive;
823 xErrorBarProp->getPropertyValue("ShowNegativeError") >>= bShowNegative;
824 bCreateXErrorBar = bShowPositive || bShowNegative;
828 Symbol* pSymbolProperties = m_bSymbol ? pSeries->getSymbolProperties( nIndex ) : nullptr;
829 bool bCreateSymbol = pSymbolProperties && (pSymbolProperties->Style != SymbolStyle_NONE);
831 if( !bCreateSymbol && !bCreateYErrorBar &&
832 !bCreateXErrorBar && !pSeries->getDataPointLabelIfLabel(nIndex) )
833 continue;
835 //create a group shape for this point and add to the series shape:
836 OUString aPointCID = ObjectIdentifier::createPointCID(
837 pSeries->getPointCID_Stub(), nIndex );
838 uno::Reference< drawing::XShapes > xPointGroupShape_Shapes(
839 createGroupShape(xSeriesGroupShape_Shapes,aPointCID) );
840 uno::Reference<drawing::XShape> xPointGroupShape_Shape( xPointGroupShape_Shapes, uno::UNO_QUERY );
843 nCreatedPoints++;
845 //create data point
846 drawing::Direction3D aSymbolSize(0,0,0);
847 if( bCreateSymbol )
849 if(m_nDimension!=3)
851 if (pSymbolProperties->Style != SymbolStyle_NONE)
853 aSymbolSize.DirectionX = pSymbolProperties->Size.Width;
854 aSymbolSize.DirectionY = pSymbolProperties->Size.Height;
857 if (pSymbolProperties->Style == SymbolStyle_STANDARD)
859 sal_Int32 nSymbol = pSymbolProperties->StandardSymbol;
860 m_pShapeFactory->createSymbol2D(
861 xPointGroupShape_Shapes, aScenePosition, aSymbolSize,
862 nSymbol, pSymbolProperties->BorderColor,
863 pSymbolProperties->FillColor);
865 else if (pSymbolProperties->Style == SymbolStyle_GRAPHIC)
867 m_pShapeFactory->createGraphic2D(xPointGroupShape_Shapes,
868 aScenePosition, aSymbolSize,
869 pSymbolProperties->Graphic);
871 //@todo other symbol styles
874 //create error bars or rectangles, depending on configuration
875 if ( ConfigAccess::getUseErrorRectangle() )
877 if ( bCreateXErrorBar || bCreateYErrorBar )
879 createErrorRectangle(
880 aUnscaledLogicPosition,
881 *pSeries,
882 nIndex,
883 m_xErrorBarTarget,
884 bCreateXErrorBar,
885 bCreateYErrorBar );
888 else
890 if (bCreateXErrorBar)
891 createErrorBar_X( aUnscaledLogicPosition, *pSeries, nIndex, m_xErrorBarTarget );
893 if (bCreateYErrorBar)
894 createErrorBar_Y( aUnscaledLogicPosition, *pSeries, nIndex, m_xErrorBarTarget, nullptr );
897 //create data point label
898 if( pSeries->getDataPointLabelIfLabel(nIndex) )
900 LabelAlignment eAlignment = LABEL_ALIGN_TOP;
901 drawing::Position3D aScenePosition3D( aScenePosition.PositionX
902 , aScenePosition.PositionY
903 , aScenePosition.PositionZ+getTransformedDepth() );
905 sal_Int32 nLabelPlacement = pSeries->getLabelPlacement(
906 nIndex, m_xChartTypeModel, rPosHelper.isSwapXAndY());
908 switch(nLabelPlacement)
910 case css::chart::DataLabelPlacement::TOP:
911 aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1);
912 eAlignment = LABEL_ALIGN_TOP;
913 break;
914 case css::chart::DataLabelPlacement::BOTTOM:
915 aScenePosition3D.PositionY += (aSymbolSize.DirectionY/2+1);
916 eAlignment = LABEL_ALIGN_BOTTOM;
917 break;
918 case css::chart::DataLabelPlacement::LEFT:
919 aScenePosition3D.PositionX -= (aSymbolSize.DirectionX/2+1);
920 eAlignment = LABEL_ALIGN_LEFT;
921 break;
922 case css::chart::DataLabelPlacement::RIGHT:
923 aScenePosition3D.PositionX += (aSymbolSize.DirectionX/2+1);
924 eAlignment = LABEL_ALIGN_RIGHT;
925 break;
926 case css::chart::DataLabelPlacement::CENTER:
927 eAlignment = LABEL_ALIGN_CENTER;
928 //todo implement this different for area charts
929 break;
930 default:
931 OSL_FAIL("this label alignment is not implemented yet");
932 aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1);
933 eAlignment = LABEL_ALIGN_TOP;
934 break;
937 awt::Point aScreenPosition2D;//get the screen position for the labels
938 sal_Int32 nOffset = 100; //todo maybe calculate this font height dependent
940 if(eAlignment==LABEL_ALIGN_CENTER || m_nDimension == 3 )
941 nOffset = 0;
942 aScreenPosition2D = LabelPositionHelper(m_nDimension,m_xLogicTarget,m_pShapeFactory)
943 .transformSceneToScreenPosition( aScenePosition3D );
946 createDataLabel( m_xTextTarget, *pSeries, nIndex
947 , fLogicValueForLabeDisplay
948 , rLogicYSumMap[nAttachedAxisIndex], aScreenPosition2D, eAlignment, nOffset );
952 //remove PointGroupShape if empty
953 if(!xPointGroupShape_Shapes->getCount())
954 xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shape);
957 }//next series in x slot (next y slot)
958 }//next x slot
959 ++nZ;
960 }//next z slot
962 impl_createSeriesShapes();
964 /* @todo remove series shapes if empty
965 //remove and delete point-group-shape if empty
966 if(!xSeriesGroupShape_Shapes->getCount())
968 pSeries->m_xShape.set(NULL);
969 m_xLogicTarget->remove(xSeriesGroupShape_Shape);
973 //remove and delete series-group-shape if empty
975 //... todo
977 SAL_INFO(
978 "chart2",
979 "skipped points: " << nSkippedPoints << " created points: "
980 << nCreatedPoints);
983 } //namespace chart
985 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */