Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / chart2 / source / view / charttypes / BarChart.cxx
blob47c137b32ab46d2db3ffe79750d6763db94d751f
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 "BarChart.hxx"
21 #include "BarPositionHelper.hxx"
23 #include <ShapeFactory.hxx>
24 #include <CommonConverters.hxx>
25 #include <ObjectIdentifier.hxx>
26 #include <LabelPositionHelper.hxx>
27 #include <AxisIndexDefines.hxx>
28 #include <Clipping.hxx>
29 #include <DateHelper.hxx>
30 #include <svx/scene3d.hxx>
31 #include <svx/unoshape.hxx>
32 #include <comphelper/scopeguard.hxx>
34 #include <com/sun/star/chart/DataLabelPlacement.hpp>
36 #include <com/sun/star/chart2/XTransformation.hpp>
37 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
38 #include <rtl/math.hxx>
39 #include <sal/log.hxx>
40 #include <tools/diagnose_ex.h>
41 #include <unordered_set>
43 namespace chart
45 using namespace ::com::sun::star;
46 using namespace ::rtl::math;
47 using namespace ::com::sun::star::chart2;
49 BarChart::BarChart( const uno::Reference<XChartType>& xChartTypeModel
50 , sal_Int32 nDimensionCount )
51 : VSeriesPlotter( xChartTypeModel, nDimensionCount )
52 , m_pMainPosHelper( new BarPositionHelper() )
54 PlotterBase::m_pPosHelper = m_pMainPosHelper.get();
55 VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper.get();
57 try
59 if( m_xChartTypeModelProps.is() )
61 m_xChartTypeModelProps->getPropertyValue( "OverlapSequence" ) >>= m_aOverlapSequence;
62 m_xChartTypeModelProps->getPropertyValue( "GapwidthSequence" ) >>= m_aGapwidthSequence;
65 catch( const uno::Exception& )
67 TOOLS_WARN_EXCEPTION("chart2", "" );
71 BarChart::~BarChart()
75 PlottingPositionHelper& BarChart::getPlottingPositionHelper( sal_Int32 nAxisIndex ) const
77 PlottingPositionHelper& rPosHelper = VSeriesPlotter::getPlottingPositionHelper( nAxisIndex );
78 BarPositionHelper* pBarPosHelper = dynamic_cast<BarPositionHelper*>(&rPosHelper);
79 if( pBarPosHelper && nAxisIndex >= 0 )
81 if( nAxisIndex < m_aOverlapSequence.getLength() )
82 pBarPosHelper->setInnerDistance( -m_aOverlapSequence[nAxisIndex]/100.0 );
83 if( nAxisIndex < m_aGapwidthSequence.getLength() )
84 pBarPosHelper->setOuterDistance( m_aGapwidthSequence[nAxisIndex]/100.0 );
86 return rPosHelper;
89 drawing::Direction3D BarChart::getPreferredDiagramAspectRatio() const
91 drawing::Direction3D aRet(1.0,1.0,1.0);
92 if( m_nDimension == 3 )
94 aRet = drawing::Direction3D(1.0,-1.0,1.0);
95 BarPositionHelper* pPosHelper = dynamic_cast<BarPositionHelper*>(&( getPlottingPositionHelper( MAIN_AXIS_INDEX) ) );
96 if (pPosHelper)
98 drawing::Direction3D aScale( pPosHelper->getScaledLogicWidth() );
99 if(aScale.DirectionX!=0.0)
101 double fXSlotCount = 1.0;
102 if(!m_aZSlots.empty())
104 fXSlotCount = m_aZSlots.begin()->size();
106 aRet.DirectionZ = aScale.DirectionZ /
107 (aScale.DirectionX + aScale.DirectionX * (fXSlotCount-1.0) * pPosHelper->getScaledSlotWidth());
109 else
111 return VSeriesPlotter::getPreferredDiagramAspectRatio();
114 else
116 return VSeriesPlotter::getPreferredDiagramAspectRatio();
119 if(aRet.DirectionZ<0.05)
121 aRet.DirectionZ=0.05;
123 else if(aRet.DirectionZ>10)
125 aRet.DirectionZ=10;
127 if( m_pMainPosHelper && m_pMainPosHelper->isSwapXAndY() )
129 double fTemp = aRet.DirectionX;
130 aRet.DirectionX = aRet.DirectionY;
131 aRet.DirectionY = fTemp;
134 else
135 aRet = drawing::Direction3D(-1,-1,-1);
136 return aRet;
139 awt::Point BarChart::getLabelScreenPositionAndAlignment(
140 LabelAlignment& rAlignment, sal_Int32 nLabelPlacement
141 , double fScaledX, double fScaledLowerYValue, double fScaledUpperYValue, double fScaledZ
142 , double fScaledLowerBarDepth, double fScaledUpperBarDepth, double fBaseValue
143 , BarPositionHelper const * pPosHelper
144 ) const
146 double fX = fScaledX;
147 double fY = fScaledUpperYValue;
148 double fZ = fScaledZ;
149 bool bReverse = !pPosHelper->isMathematicalOrientationY();
150 bool bNormalOutside = (!bReverse == (fBaseValue < fScaledUpperYValue));
151 double fDepth = fScaledUpperBarDepth;
153 switch(nLabelPlacement)
155 case css::chart::DataLabelPlacement::TOP:
157 if( !pPosHelper->isSwapXAndY() )
159 fY = bReverse ? fScaledLowerYValue : fScaledUpperYValue;
160 rAlignment = LABEL_ALIGN_TOP;
161 if(m_nDimension==3)
162 fDepth = bReverse ? fabs(fScaledLowerBarDepth) : fabs(fScaledUpperBarDepth);
164 else
166 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
167 rAlignment = LABEL_ALIGN_CENTER;
168 OSL_FAIL( "top label placement is not really supported by horizontal bar charts" );
171 break;
172 case css::chart::DataLabelPlacement::BOTTOM:
174 if(!pPosHelper->isSwapXAndY())
176 fY = bReverse ? fScaledUpperYValue : fScaledLowerYValue;
177 rAlignment = LABEL_ALIGN_BOTTOM;
178 if(m_nDimension==3)
179 fDepth = bReverse ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth);
181 else
183 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
184 rAlignment = LABEL_ALIGN_CENTER;
185 OSL_FAIL( "bottom label placement is not supported by horizontal bar charts" );
188 break;
189 case css::chart::DataLabelPlacement::LEFT:
191 if( pPosHelper->isSwapXAndY() )
193 fY = bReverse ? fScaledUpperYValue : fScaledLowerYValue;
194 rAlignment = LABEL_ALIGN_LEFT;
195 if(m_nDimension==3)
196 fDepth = bReverse ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth);
198 else
200 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
201 rAlignment = LABEL_ALIGN_CENTER;
202 OSL_FAIL( "left label placement is not supported by column charts" );
205 break;
206 case css::chart::DataLabelPlacement::RIGHT:
208 if( pPosHelper->isSwapXAndY() )
210 fY = bReverse ? fScaledLowerYValue : fScaledUpperYValue;
211 rAlignment = LABEL_ALIGN_RIGHT;
212 if(m_nDimension==3)
213 fDepth = bReverse ? fabs(fScaledLowerBarDepth) : fabs(fScaledUpperBarDepth);
215 else
217 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
218 rAlignment = LABEL_ALIGN_CENTER;
219 OSL_FAIL( "right label placement is not supported by column charts" );
222 break;
223 case css::chart::DataLabelPlacement::OUTSIDE:
225 fY = (fBaseValue < fScaledUpperYValue) ? fScaledUpperYValue : fScaledLowerYValue;
226 if( pPosHelper->isSwapXAndY() )
227 // if datapoint value is 0 the label will appear RIGHT in case of Bar Chart
228 if( fBaseValue == fScaledUpperYValue && fBaseValue == fScaledLowerYValue )
229 rAlignment = LABEL_ALIGN_RIGHT;
230 else
231 rAlignment = bNormalOutside ? LABEL_ALIGN_RIGHT : LABEL_ALIGN_LEFT;
232 else
233 // if datapoint value is 0 the label will appear TOP in case of Column Chart
234 if( fBaseValue == fScaledUpperYValue && fBaseValue == fScaledLowerYValue )
235 rAlignment = LABEL_ALIGN_TOP;
236 else
237 rAlignment = bNormalOutside ? LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM;
238 if(m_nDimension==3)
239 fDepth = (fBaseValue < fScaledUpperYValue) ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth);
241 break;
242 case css::chart::DataLabelPlacement::INSIDE:
244 fY = (fBaseValue < fScaledUpperYValue) ? fScaledUpperYValue : fScaledLowerYValue;
245 if( pPosHelper->isSwapXAndY() )
246 rAlignment = bNormalOutside ? LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT;
247 else
248 rAlignment = bNormalOutside ? LABEL_ALIGN_BOTTOM : LABEL_ALIGN_TOP;
249 if(m_nDimension==3)
250 fDepth = (fBaseValue < fScaledUpperYValue) ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth);
252 break;
253 case css::chart::DataLabelPlacement::NEAR_ORIGIN:
255 fY = (fBaseValue < fScaledUpperYValue) ? fScaledLowerYValue : fScaledUpperYValue;
256 if( pPosHelper->isSwapXAndY() )
257 // if datapoint value is 0 the label will appear RIGHT in case of Bar Chart
258 if( fBaseValue == fScaledUpperYValue && fBaseValue == fScaledLowerYValue )
259 rAlignment = LABEL_ALIGN_RIGHT;
260 else
261 rAlignment = bNormalOutside ? LABEL_ALIGN_RIGHT : LABEL_ALIGN_LEFT;
262 else
263 // if datapoint value is 0 the label will appear TOP in case of Column Chart
264 if( fBaseValue == fScaledUpperYValue && fBaseValue == fScaledLowerYValue )
265 rAlignment = LABEL_ALIGN_TOP;
266 else
267 rAlignment = bNormalOutside ? LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM;
268 if(m_nDimension==3)
269 fDepth = (fBaseValue < fScaledUpperYValue) ? fabs(fScaledLowerBarDepth) : fabs(fScaledUpperBarDepth);
271 break;
272 case css::chart::DataLabelPlacement::CENTER:
273 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
274 // if datapoint value is 0 the label will appear TOP/RIGHT in case of Column/Bar Charts
275 if( fBaseValue == fScaledUpperYValue && fBaseValue == fScaledLowerYValue )
276 if( pPosHelper->isSwapXAndY() )
277 rAlignment = LABEL_ALIGN_RIGHT;
278 else
279 rAlignment = LABEL_ALIGN_TOP;
280 else
281 rAlignment = LABEL_ALIGN_CENTER;
282 if(m_nDimension==3)
283 fDepth = fabs(fScaledUpperBarDepth-fScaledLowerBarDepth)/2.0;
284 break;
285 default:
286 OSL_FAIL("this label alignment is not implemented yet");
288 break;
290 if(m_nDimension==3)
291 fZ -= fDepth/2.0;
293 drawing::Position3D aScenePosition3D( pPosHelper->
294 transformScaledLogicToScene( fX, fY, fZ, true ) );
295 return LabelPositionHelper(m_nDimension,m_xLogicTarget,m_pShapeFactory)
296 .transformSceneToScreenPosition( aScenePosition3D );
299 uno::Reference< drawing::XShape > BarChart::createDataPoint3D_Bar(
300 const uno::Reference< drawing::XShapes >& xTarget
301 , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
302 , double fTopHeight, sal_Int32 nRotateZAngleHundredthDegree
303 , const uno::Reference< beans::XPropertySet >& xObjectProperties
304 , sal_Int32 nGeometry3D )
306 bool bRoundedEdges = true;
309 if( xObjectProperties.is() )
311 sal_Int16 nPercentDiagonal = 0;
312 xObjectProperties->getPropertyValue( "PercentDiagonal" ) >>= nPercentDiagonal;
313 if( nPercentDiagonal < 5 )
314 bRoundedEdges = false;
317 catch( const uno::Exception& )
319 TOOLS_WARN_EXCEPTION("chart2", "" );
322 uno::Reference< drawing::XShape > xShape;
323 switch( nGeometry3D )
325 case DataPointGeometry3D::CYLINDER:
326 xShape = m_pShapeFactory->createCylinder( xTarget, rPosition, rSize, nRotateZAngleHundredthDegree );
327 break;
328 case DataPointGeometry3D::CONE:
329 xShape = m_pShapeFactory->createCone( xTarget, rPosition, rSize, fTopHeight, nRotateZAngleHundredthDegree );
330 break;
331 case DataPointGeometry3D::PYRAMID:
332 xShape = m_pShapeFactory->createPyramid( xTarget, rPosition, rSize, fTopHeight, nRotateZAngleHundredthDegree>0
333 , xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
334 break;
335 case DataPointGeometry3D::CUBOID:
336 default:
337 xShape = m_pShapeFactory->createCube( xTarget, rPosition, rSize
338 , nRotateZAngleHundredthDegree, xObjectProperties
339 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), bRoundedEdges );
340 return xShape;
342 if( nGeometry3D != DataPointGeometry3D::PYRAMID )
343 setMappedProperties( xShape, xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
344 return xShape;
347 namespace
349 bool lcl_hasGeometry3DVariableWidth( sal_Int32 nGeometry3D )
351 bool bRet = false;
352 switch( nGeometry3D )
354 case DataPointGeometry3D::PYRAMID:
355 case DataPointGeometry3D::CONE:
356 bRet = true;
357 break;
358 case DataPointGeometry3D::CUBOID:
359 case DataPointGeometry3D::CYLINDER:
360 default:
361 bRet = false;
362 break;
364 return bRet;
366 }// end anonymous namespace
368 void BarChart::addSeries( std::unique_ptr<VDataSeries> pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot )
370 if( !pSeries )
371 return;
372 if(m_nDimension==2)
374 //2ND_AXIS_IN_BARS put series on second scales to different z slot as temporary workaround
375 //this needs to be redesigned if 3d bars are also able to display secondary axes
377 sal_Int32 nAxisIndex = pSeries->getAttachedAxisIndex();
378 zSlot = nAxisIndex;
380 if( !pSeries->getGroupBarsPerAxis() )
381 zSlot = 0;
382 if(zSlot>=static_cast<sal_Int32>(m_aZSlots.size()))
383 m_aZSlots.resize(zSlot+1);
385 VSeriesPlotter::addSeries( std::move(pSeries), zSlot, xSlot, ySlot );
388 //better performance for big data
389 struct FormerBarPoint
391 FormerBarPoint( double fX, double fUpperY, double fLowerY, double fZ )
392 : m_fX(fX), m_fUpperY(fUpperY), m_fLowerY(fLowerY), m_fZ(fZ)
394 FormerBarPoint()
396 ::rtl::math::setNan( &m_fX );
397 ::rtl::math::setNan( &m_fUpperY );
398 ::rtl::math::setNan( &m_fLowerY );
399 ::rtl::math::setNan( &m_fZ );
402 double m_fX;
403 double m_fUpperY;
404 double m_fLowerY;
405 double m_fZ;
408 void BarChart::adaptOverlapAndGapwidthForGroupBarsPerAxis()
410 //adapt m_aOverlapSequence and m_aGapwidthSequence for the groupBarsPerAxis feature
411 //thus the different series use the same settings
413 VDataSeries* pFirstSeries = getFirstSeries();
414 if(pFirstSeries && !pFirstSeries->getGroupBarsPerAxis() )
416 sal_Int32 nAxisIndex = pFirstSeries->getAttachedAxisIndex();
417 sal_Int32 nN = 0;
418 sal_Int32 nUseThisIndex = nAxisIndex;
419 if( nUseThisIndex < 0 || nUseThisIndex >= m_aOverlapSequence.getLength() )
420 nUseThisIndex = 0;
421 for( nN = 0; nN < m_aOverlapSequence.getLength(); nN++ )
423 if(nN!=nUseThisIndex)
424 m_aOverlapSequence[nN] = m_aOverlapSequence[nUseThisIndex];
427 nUseThisIndex = nAxisIndex;
428 if( nUseThisIndex < 0 || nUseThisIndex >= m_aGapwidthSequence.getLength() )
429 nUseThisIndex = 0;
430 for( nN = 0; nN < m_aGapwidthSequence.getLength(); nN++ )
432 if(nN!=nUseThisIndex)
433 m_aGapwidthSequence[nN] = m_aGapwidthSequence[nUseThisIndex];
438 static E3dScene* lcl_getE3dScene(uno::Reference<uno::XInterface> const & xInterface)
440 E3dScene* pScene = nullptr;
442 SvxShape* pSvxShape = comphelper::getUnoTunnelImplementation<SvxShape>(xInterface);
443 if (pSvxShape)
445 SdrObject* pObject = pSvxShape->GetSdrObject();
446 pScene = dynamic_cast<E3dScene*>(pObject);
448 return pScene;
451 void BarChart::createShapes()
453 if( m_aZSlots.empty() ) //no series
454 return;
456 OSL_ENSURE(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"BarChart is not proper initialized");
457 if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is()))
458 return;
460 //the text labels should be always on top of the other series shapes
461 //therefore create an own group for the texts to move them to front
462 //(because the text group is created after the series group the texts are displayed on top)
464 //the regression curves should always be on top of the bars but beneath the text labels
465 //to achieve this the regression curve target is created after the series target and before the text target
467 uno::Reference< drawing::XShapes > xSeriesTarget(
468 createGroupShape( m_xLogicTarget ));
469 uno::Reference< drawing::XShapes > xRegressionCurveTarget(
470 createGroupShape( m_xLogicTarget ));
471 uno::Reference< drawing::XShapes > xTextTarget(
472 m_pShapeFactory->createGroup2D( m_xFinalTarget ));
474 uno::Reference< drawing::XShapes > xRegressionCurveEquationTarget(
475 m_pShapeFactory->createGroup2D( m_xFinalTarget ));
476 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
478 double fLogicZ = 1.0;//as defined
480 bool bDrawConnectionLines = false;
481 bool bDrawConnectionLinesInited = false;
482 bool bOnlyConnectionLinesForThisPoint = false;
484 std::unordered_set<uno::Reference<drawing::XShape>> aShapeSet;
486 const comphelper::ScopeGuard aGuard([aShapeSet]() {
488 std::unordered_set<E3dScene*> aSceneSet;
490 for (uno::Reference<drawing::XShape> const & rShape : aShapeSet)
492 E3dScene* pScene = lcl_getE3dScene(rShape);
493 if(nullptr != pScene)
495 aSceneSet.insert(pScene->getRootE3dSceneFromE3dObject());
498 for (E3dScene* pScene : aSceneSet)
500 pScene->ResumeReportingDirtyRects();
501 pScene->SetAllSceneRectsDirty();
505 adaptOverlapAndGapwidthForGroupBarsPerAxis();
507 //better performance for big data
508 std::map< VDataSeries*, FormerBarPoint > aSeriesFormerPointMap;
509 m_bPointsWereSkipped = false;
510 sal_Int32 nSkippedPoints = 0;
511 sal_Int32 nCreatedPoints = 0;
513 sal_Int32 nStartIndex = 0;
514 sal_Int32 nEndIndex = VSeriesPlotter::getPointCount();
515 //iterate through all x values per indices
516 for( sal_Int32 nPointIndex = nStartIndex; nPointIndex < nEndIndex; nPointIndex++ )
518 //sum up the values for all series in a complete z slot per attached axis
519 std::map< sal_Int32, double > aLogicYSumMap;
520 for( auto& rZSlot : m_aZSlots )
522 for( auto& rXSlot : rZSlot )
524 sal_Int32 nAttachedAxisIndex = rXSlot.getAttachedAxisIndexForFirstSeries();
525 if( aLogicYSumMap.find(nAttachedAxisIndex)==aLogicYSumMap.end() )
526 aLogicYSumMap[nAttachedAxisIndex]=0.0;
528 const sal_Int32 nSlotPoints = rXSlot.getPointCount();
529 if( nPointIndex >= nSlotPoints )
530 continue;
532 double fMinimumY = 0.0, fMaximumY = 0.0;
533 rXSlot.calculateYMinAndMaxForCategory( nPointIndex
534 , isSeparateStackingForDifferentSigns( 1 ), fMinimumY, fMaximumY, nAttachedAxisIndex );
536 if( !::rtl::math::isNan( fMaximumY ) && fMaximumY > 0)
537 aLogicYSumMap[nAttachedAxisIndex] += fMaximumY;
538 if( !::rtl::math::isNan( fMinimumY ) && fMinimumY < 0)
539 aLogicYSumMap[nAttachedAxisIndex] += fabs(fMinimumY);
543 sal_Int32 nZ=1;
544 for( auto& rZSlot : m_aZSlots )
546 //iterate through all x slots in this category
547 double fSlotX=0;
548 for( auto& rXSlot : rZSlot )
550 sal_Int32 nAttachedAxisIndex = rXSlot.getAttachedAxisIndexForFirstSeries();
551 //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot
552 BarPositionHelper* pPosHelper = dynamic_cast<BarPositionHelper*>(&( getPlottingPositionHelper( nAttachedAxisIndex ) ) );
553 if(!pPosHelper)
554 pPosHelper = m_pMainPosHelper.get();
556 PlotterBase::m_pPosHelper = pPosHelper;
558 //update/create information for current group
559 pPosHelper->updateSeriesCount( rZSlot.size() );
560 double fLogicBaseWidth = pPosHelper->getScaledSlotWidth();
562 // get distance from base value to maximum and minimum
564 double fMinimumY = 0.0, fMaximumY = 0.0;
565 if( nPointIndex < rXSlot.getPointCount())
566 rXSlot.calculateYMinAndMaxForCategory( nPointIndex
567 , isSeparateStackingForDifferentSigns( 1 ), fMinimumY, fMaximumY, nAttachedAxisIndex );
569 double fLogicPositiveYSum = 0.0;
570 if( !::rtl::math::isNan( fMaximumY ) )
571 fLogicPositiveYSum = fMaximumY;
573 double fLogicNegativeYSum = 0.0;
574 if( !::rtl::math::isNan( fMinimumY ) )
575 fLogicNegativeYSum = fMinimumY;
577 if( pPosHelper->isPercentY() )
579 /* #i70395# fLogicPositiveYSum contains sum of all positive
580 values, if any, otherwise the highest negative value.
581 fLogicNegativeYSum contains sum of all negative values,
582 if any, otherwise the lowest positive value.
583 Afterwards, fLogicPositiveYSum will contain the maximum
584 (positive) value that is related to 100%. */
586 // do nothing if there are positive values only
587 if( fLogicNegativeYSum < 0.0 )
589 // fLogicPositiveYSum<0 => negative values only, use absolute of negative sum
590 if( fLogicPositiveYSum < 0.0 )
591 fLogicPositiveYSum = -fLogicNegativeYSum;
592 // otherwise there are positive and negative values, calculate total distance
593 else
594 fLogicPositiveYSum -= fLogicNegativeYSum;
596 fLogicNegativeYSum = 0.0;
599 double fBaseValue = 0.0;
600 if( !pPosHelper->isPercentY() && rXSlot.m_aSeriesVector.size()<=1 )
601 fBaseValue = pPosHelper->getBaseValueY();
602 double fPositiveLogicYForNextSeries = fBaseValue;
603 double fNegativeLogicYForNextSeries = fBaseValue;
605 //iterate through all series in this x slot
606 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
608 if(!pSeries)
609 continue;
611 bool bHasFillColorMapping = pSeries->hasPropertyMapping("FillColor");
613 bOnlyConnectionLinesForThisPoint = false;
615 if(nPointIndex==nStartIndex)//do not create a regression line for each point
616 createRegressionCurvesShapes( *pSeries, xRegressionCurveTarget, xRegressionCurveEquationTarget,
617 m_pPosHelper->maySkipPointsInRegressionCalculation());
619 if( !bDrawConnectionLinesInited )
621 bDrawConnectionLines = pSeries->getConnectBars();
622 if( m_nDimension==3 )
623 bDrawConnectionLines = false;
624 if( bDrawConnectionLines && rXSlot.m_aSeriesVector.size()==1 )
626 //detect whether we have a stacked chart or not:
627 StackingDirection eDirection = pSeries->getStackingDirection();
628 if( eDirection != StackingDirection_Y_STACKING )
629 bDrawConnectionLines = false;
631 bDrawConnectionLinesInited = true;
634 uno::Reference<drawing::XShapes> xSeriesGroupShape_Shapes(getSeriesGroupShape(pSeries.get(), xSeriesTarget));
635 uno::Reference<drawing::XShape> xSeriesGroupShape(xSeriesGroupShape_Shapes, uno::UNO_QUERY);
636 // Suspend setting rects dirty for the duration of this call
637 aShapeSet.insert(xSeriesGroupShape);
638 E3dScene* pScene = lcl_getE3dScene(xSeriesGroupShape);
639 if (pScene)
640 pScene->SuspendReportingDirtyRects();
642 //collect data point information (logic coordinates, style ):
643 double fUnscaledLogicX = pSeries->getXValue( nPointIndex );
644 fUnscaledLogicX = DateHelper::RasterizeDateValue( fUnscaledLogicX, m_aNullDate, m_nTimeResolution );
645 if(rtl::math::isNan(fUnscaledLogicX))
646 continue;//point not visible
647 if(fUnscaledLogicX<pPosHelper->getLogicMinX())
648 continue;//point not visible
649 if(fUnscaledLogicX>pPosHelper->getLogicMaxX())
650 continue;//point not visible
651 if(pPosHelper->isStrongLowerRequested(0) && fUnscaledLogicX==pPosHelper->getLogicMaxX())
652 continue;//point not visible
653 double fLogicX = pPosHelper->getScaledSlotPos( fUnscaledLogicX, fSlotX );
655 double fLogicBarHeight = pSeries->getYValue( nPointIndex );
656 if( ::rtl::math::isNan( fLogicBarHeight )) //no value at this category
657 continue;
659 double fLogicValueForLabeDisplay = fLogicBarHeight;
660 fLogicBarHeight-=fBaseValue;
662 if( pPosHelper->isPercentY() )
664 if(fLogicPositiveYSum!=0.0)
665 fLogicBarHeight = fabs( fLogicBarHeight )/fLogicPositiveYSum;
666 else
667 fLogicBarHeight = 0.0;
670 // tdf#114141 to draw the top of the zero height 3D bar
671 // we set a small positive value, here the smallest one for the type double (DBL_MIN)
672 if( fLogicBarHeight == 0.0 )
673 fLogicBarHeight = DBL_MIN;
675 //sort negative and positive values, to display them on different sides of the x axis
676 bool bPositive = fLogicBarHeight >= 0.0;
677 double fLowerYValue = bPositive ? fPositiveLogicYForNextSeries : fNegativeLogicYForNextSeries;
678 double fUpperYValue = fLowerYValue+fLogicBarHeight;
679 if( bPositive )
680 fPositiveLogicYForNextSeries += fLogicBarHeight;
681 else
682 fNegativeLogicYForNextSeries += fLogicBarHeight;
684 if(m_nDimension==3)
685 fLogicZ = nZ+0.5;
687 drawing::Position3D aUnscaledLogicPosition( fUnscaledLogicX, fUpperYValue, fLogicZ );
689 //@todo ... start an iteration over the different breaks of the axis
690 //each subsystem may add an additional shape to form the whole point
691 //create a group shape for this point and add to the series shape:
692 // uno::Reference< drawing::XShapes > xPointGroupShape_Shapes( createGroupShape(xSeriesGroupShape_Shapes) );
693 // uno::Reference<drawing::XShape> xPointGroupShape_Shape =
694 // uno::Reference<drawing::XShape>( xPointGroupShape_Shapes, uno::UNO_QUERY );
695 //as long as we do not iterate we do not need to create an additional group for each point
696 uno::Reference< beans::XPropertySet > xDataPointProperties( pSeries->getPropertiesOfPoint( nPointIndex ) );
697 sal_Int32 nGeometry3D = DataPointGeometry3D::CUBOID;
698 if(m_nDimension==3) try
700 xDataPointProperties->getPropertyValue( "Geometry3D") >>= nGeometry3D;
702 catch( const uno::Exception& )
704 TOOLS_WARN_EXCEPTION("chart2", "" );
707 //@todo iterate through all subsystems to create partial points
709 //@todo select a suitable PositionHelper for this subsystem
710 BarPositionHelper* pSubPosHelper = pPosHelper;
712 double fUnclippedUpperYValue = fUpperYValue;
714 //apply clipping to Y
715 if( !pPosHelper->clipYRange(fLowerYValue,fUpperYValue) )
717 if( bDrawConnectionLines )
718 bOnlyConnectionLinesForThisPoint = true;
719 else
720 continue;
722 //@todo clipping of X and Z is not fully integrated so far, as there is a need to create different objects
724 //apply scaling to Y before calculating width (necessary to maintain gradient in clipped objects)
725 pSubPosHelper->doLogicScaling(nullptr,&fLowerYValue,nullptr);
726 pSubPosHelper->doLogicScaling(nullptr,&fUpperYValue,nullptr);
727 //scaling of X and Z is not provided as the created objects should be symmetric in that dimensions
729 pSubPosHelper->doLogicScaling(nullptr,&fUnclippedUpperYValue,nullptr);
731 //calculate resulting width
732 double fCompleteHeight = bPositive ? fLogicPositiveYSum : fLogicNegativeYSum;
733 if( pPosHelper->isPercentY() )
734 fCompleteHeight = 1.0;
735 double fLogicBarWidth = fLogicBaseWidth;
736 double fTopHeight=approxSub(fCompleteHeight,fUpperYValue);
737 if(!bPositive)
738 fTopHeight=approxSub(fCompleteHeight,fLowerYValue);
739 double fLogicYStart = bPositive ? fLowerYValue : fUpperYValue;
740 double fMiddleHeight = fUpperYValue-fLowerYValue;
741 if(!bPositive)
742 fMiddleHeight*=-1.0;
743 double fLogicBarDepth = 0.5;
744 if(m_nDimension==3)
746 if( lcl_hasGeometry3DVariableWidth(nGeometry3D) && fCompleteHeight!=0.0 )
748 double fHeight = fCompleteHeight-fLowerYValue;
749 if(!bPositive)
750 fHeight = fCompleteHeight-fUpperYValue;
751 fLogicBarWidth = fLogicBaseWidth*fHeight/fCompleteHeight;
752 if(fLogicBarWidth<=0.0)
753 fLogicBarWidth=fLogicBaseWidth;
754 fLogicBarDepth = fLogicBarDepth*fHeight/fCompleteHeight;
755 if(fLogicBarDepth<=0.0)
756 fLogicBarDepth*=-1.0;
760 //better performance for big data
761 FormerBarPoint aFormerPoint( aSeriesFormerPointMap[pSeries.get()] );
762 pPosHelper->setCoordinateSystemResolution( m_aCoordinateSystemResolution );
763 if( !pSeries->isAttributedDataPoint(nPointIndex)
765 pPosHelper->isSameForGivenResolution( aFormerPoint.m_fX, aFormerPoint.m_fUpperY, aFormerPoint.m_fZ
766 , fLogicX, fUpperYValue, fLogicZ )
768 pPosHelper->isSameForGivenResolution( aFormerPoint.m_fX, aFormerPoint.m_fLowerY, aFormerPoint.m_fZ
769 , fLogicX, fLowerYValue, fLogicZ )
772 nSkippedPoints++;
773 m_bPointsWereSkipped = true;
774 continue;
776 aSeriesFormerPointMap[pSeries.get()] = FormerBarPoint(fLogicX,fUpperYValue,fLowerYValue,fLogicZ);
778 if( bDrawConnectionLines )
780 //store point information for connection lines
782 drawing::Position3D aLeftUpperPoint( fLogicX-fLogicBarWidth/2.0,fUnclippedUpperYValue,fLogicZ );
783 drawing::Position3D aRightUpperPoint( fLogicX+fLogicBarWidth/2.0,fUnclippedUpperYValue,fLogicZ );
785 if( isValidPosition(aLeftUpperPoint) )
786 AddPointToPoly( pSeries->m_aPolyPolygonShape3D, aLeftUpperPoint );
787 if( isValidPosition(aRightUpperPoint) )
788 AddPointToPoly( pSeries->m_aPolyPolygonShape3D, aRightUpperPoint );
791 if( bOnlyConnectionLinesForThisPoint )
792 continue;
794 //maybe additional possibility for performance improvement
795 //bool bCreateLineInsteadOfComplexGeometryDueToMissingSpace = false;
796 //pPosHelper->isSameForGivenResolution( fLogicX-fLogicBarWidth/2.0, fLowerYValue, fLogicZ
797 // , fLogicX+fLogicBarWidth/2.0, fLowerYValue, fLogicZ );
799 nCreatedPoints++;
800 //create partial point
801 if( !approxEqual(fLowerYValue,fUpperYValue) )
803 uno::Reference< drawing::XShape > xShape;
804 if( m_nDimension==3 )
806 drawing::Position3D aLogicBottom (fLogicX,fLogicYStart,fLogicZ);
807 drawing::Position3D aLogicLeftBottomFront (fLogicX+fLogicBarWidth/2.0,fLogicYStart,fLogicZ-fLogicBarDepth/2.0);
808 drawing::Position3D aLogicRightDeepTop (fLogicX-fLogicBarWidth/2.0,fLogicYStart+fMiddleHeight,fLogicZ+fLogicBarDepth/2.0);
809 drawing::Position3D aLogicTopTop (fLogicX,fLogicYStart+fMiddleHeight+fTopHeight,fLogicZ);
811 uno::Reference< XTransformation > xTransformation = pSubPosHelper->getTransformationScaledLogicToScene();
813 //transformation 3) -> 4)
814 drawing::Position3D aTransformedBottom ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicBottom) ) ) );
815 drawing::Position3D aTransformedLeftBottomFront ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicLeftBottomFront) ) ) );
816 drawing::Position3D aTransformedRightDeepTop ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicRightDeepTop) ) ) );
817 drawing::Position3D aTransformedTopTop ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicTopTop) ) ) );
819 drawing::Direction3D aSize = aTransformedRightDeepTop - aTransformedLeftBottomFront;
820 drawing::Direction3D aTopSize( aTransformedTopTop - aTransformedRightDeepTop );
821 fTopHeight = aTopSize.DirectionY;
823 sal_Int32 nRotateZAngleHundredthDegree = 0;
824 if( pPosHelper->isSwapXAndY() )
826 fTopHeight = aTopSize.DirectionX;
827 nRotateZAngleHundredthDegree = 90*100;
828 aSize = drawing::Direction3D(aSize.DirectionY,aSize.DirectionX,aSize.DirectionZ);
831 if( aSize.DirectionX < 0 )
832 aSize.DirectionX *= -1.0;
833 if( aSize.DirectionZ < 0 )
834 aSize.DirectionZ *= -1.0;
835 if( fTopHeight < 0 )
836 fTopHeight *= -1.0;
838 xShape = createDataPoint3D_Bar(
839 xSeriesGroupShape_Shapes, aTransformedBottom, aSize, fTopHeight, nRotateZAngleHundredthDegree
840 , xDataPointProperties, nGeometry3D );
842 else //m_nDimension!=3
844 // performance improvement: alloc the sequence before the rendering
845 // otherwise we have 2 realloc calls
846 drawing::PolyPolygonShape3D aPoly;
847 aPoly.SequenceX.realloc(1);
848 aPoly.SequenceY.realloc(1);
849 aPoly.SequenceZ.realloc(1);
850 drawing::Position3D aLeftUpperPoint( fLogicX-fLogicBarWidth/2.0,fUpperYValue,fLogicZ );
851 drawing::Position3D aRightUpperPoint( fLogicX+fLogicBarWidth/2.0,fUpperYValue,fLogicZ );
853 AddPointToPoly( aPoly, drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) );
854 AddPointToPoly( aPoly, drawing::Position3D( fLogicX+fLogicBarWidth/2.0,fLowerYValue,fLogicZ) );
855 AddPointToPoly( aPoly, aRightUpperPoint );
856 AddPointToPoly( aPoly, aLeftUpperPoint );
857 AddPointToPoly( aPoly, drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) );
858 pPosHelper->transformScaledLogicToScene( aPoly );
859 xShape = m_pShapeFactory->createArea2D( xSeriesGroupShape_Shapes, aPoly );
860 setMappedProperties( xShape, xDataPointProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
863 if(bHasFillColorMapping)
865 double nPropVal = pSeries->getValueByProperty(nPointIndex, "FillColor");
866 if(!rtl::math::isNan(nPropVal))
868 uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY_THROW );
869 xProps->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>(nPropVal)));
872 //set name/classified ObjectID (CID)
873 ShapeFactory::setShapeName(xShape
874 , ObjectIdentifier::createPointCID(
875 pSeries->getPointCID_Stub(),nPointIndex) );
878 //create error bar
879 createErrorBar_Y( aUnscaledLogicPosition, *pSeries, nPointIndex, m_xLogicTarget, &fLogicX );
881 //create data point label
882 if( pSeries->getDataPointLabelIfLabel(nPointIndex) )
884 double fLogicSum = aLogicYSumMap[nAttachedAxisIndex];
886 LabelAlignment eAlignment(LABEL_ALIGN_CENTER);
887 sal_Int32 nLabelPlacement = pSeries->getLabelPlacement( nPointIndex, m_xChartTypeModel, pPosHelper->isSwapXAndY() );
889 double fLowerBarDepth = fLogicBarDepth;
890 double fUpperBarDepth = fLogicBarDepth;
892 if( lcl_hasGeometry3DVariableWidth(nGeometry3D) && fCompleteHeight!=0.0 )
894 double fOuterBarDepth = fLogicBarDepth * fTopHeight/(fabs(fCompleteHeight));
895 fLowerBarDepth = (fBaseValue < fUpperYValue) ? fabs(fLogicBarDepth) : fabs(fOuterBarDepth);
896 fUpperBarDepth = (fBaseValue < fUpperYValue) ? fabs(fOuterBarDepth) : fabs(fLogicBarDepth);
900 awt::Point aScreenPosition2D = getLabelScreenPositionAndAlignment(
901 eAlignment, nLabelPlacement, fLogicX, fLowerYValue, fUpperYValue, fLogicZ,
902 fLowerBarDepth, fUpperBarDepth, fBaseValue, pPosHelper);
903 sal_Int32 nOffset = 0;
904 if(eAlignment!=LABEL_ALIGN_CENTER)
906 nOffset = 100;//add some spacing //@todo maybe get more intelligent values
907 if( m_nDimension == 3 )
908 nOffset = 260;
910 createDataLabel(
911 xTextTarget, *pSeries, nPointIndex,
912 fLogicValueForLabeDisplay, fLogicSum, aScreenPosition2D, eAlignment, nOffset);
915 }//end iteration through partial points
917 }//next series in x slot (next y slot)
918 fSlotX+=1.0;
919 }//next x slot
920 ++nZ;
921 }//next z slot
922 }//next category
923 if( bDrawConnectionLines )
925 for( auto const& rZSlot : m_aZSlots )
927 BarPositionHelper* pPosHelper = m_pMainPosHelper.get();
928 if( !rZSlot.empty() )
930 sal_Int32 nAttachedAxisIndex = rZSlot.front().getAttachedAxisIndexForFirstSeries();
931 //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot
932 pPosHelper = dynamic_cast<BarPositionHelper*>(&( getPlottingPositionHelper( nAttachedAxisIndex ) ) );
933 if(!pPosHelper)
934 pPosHelper = m_pMainPosHelper.get();
936 PlotterBase::m_pPosHelper = pPosHelper;
938 //iterate through all x slots in this category
939 for( auto const& rXSlot : rZSlot )
941 //iterate through all series in this x slot
942 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
944 if(!pSeries)
945 continue;
946 drawing::PolyPolygonShape3D* pSeriesPoly = &pSeries->m_aPolyPolygonShape3D;
947 if(!ShapeFactory::hasPolygonAnyLines(*pSeriesPoly))
948 continue;
950 drawing::PolyPolygonShape3D aPoly;
951 Clipping::clipPolygonAtRectangle( *pSeriesPoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
953 if(!ShapeFactory::hasPolygonAnyLines(aPoly))
954 continue;
956 //transformation 3) -> 4)
957 pPosHelper->transformScaledLogicToScene( aPoly );
959 uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes(
960 getSeriesGroupShape(pSeries.get(), xSeriesTarget) );
961 uno::Reference< drawing::XShape > xShape( m_pShapeFactory->createLine2D(
962 xSeriesGroupShape_Shapes, PolyToPointSequence( aPoly ) ) );
963 setMappedProperties( xShape, pSeries->getPropertiesOfSeries()
964 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
970 /* @todo remove series shapes if empty
973 SAL_INFO(
974 "chart2",
975 "skipped points: " << nSkippedPoints << " created points: "
976 << nCreatedPoints);
979 } //namespace chart
981 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */