1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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();
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", "" );
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 );
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
) ) );
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());
111 return VSeriesPlotter::getPreferredDiagramAspectRatio();
116 return VSeriesPlotter::getPreferredDiagramAspectRatio();
119 if(aRet
.DirectionZ
<0.05)
121 aRet
.DirectionZ
=0.05;
123 else if(aRet
.DirectionZ
>10)
127 if( m_pMainPosHelper
&& m_pMainPosHelper
->isSwapXAndY() )
129 double fTemp
= aRet
.DirectionX
;
130 aRet
.DirectionX
= aRet
.DirectionY
;
131 aRet
.DirectionY
= fTemp
;
135 aRet
= drawing::Direction3D(-1,-1,-1);
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
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
;
162 fDepth
= bReverse
? fabs(fScaledLowerBarDepth
) : fabs(fScaledUpperBarDepth
);
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" );
172 case css::chart::DataLabelPlacement::BOTTOM
:
174 if(!pPosHelper
->isSwapXAndY())
176 fY
= bReverse
? fScaledUpperYValue
: fScaledLowerYValue
;
177 rAlignment
= LABEL_ALIGN_BOTTOM
;
179 fDepth
= bReverse
? fabs(fScaledUpperBarDepth
) : fabs(fScaledLowerBarDepth
);
183 fY
-= (fScaledUpperYValue
-fScaledLowerYValue
)/2.0;
184 rAlignment
= LABEL_ALIGN_CENTER
;
185 OSL_FAIL( "bottom label placement is not supported by horizontal bar charts" );
189 case css::chart::DataLabelPlacement::LEFT
:
191 if( pPosHelper
->isSwapXAndY() )
193 fY
= bReverse
? fScaledUpperYValue
: fScaledLowerYValue
;
194 rAlignment
= LABEL_ALIGN_LEFT
;
196 fDepth
= bReverse
? fabs(fScaledUpperBarDepth
) : fabs(fScaledLowerBarDepth
);
200 fY
-= (fScaledUpperYValue
-fScaledLowerYValue
)/2.0;
201 rAlignment
= LABEL_ALIGN_CENTER
;
202 OSL_FAIL( "left label placement is not supported by column charts" );
206 case css::chart::DataLabelPlacement::RIGHT
:
208 if( pPosHelper
->isSwapXAndY() )
210 fY
= bReverse
? fScaledLowerYValue
: fScaledUpperYValue
;
211 rAlignment
= LABEL_ALIGN_RIGHT
;
213 fDepth
= bReverse
? fabs(fScaledLowerBarDepth
) : fabs(fScaledUpperBarDepth
);
217 fY
-= (fScaledUpperYValue
-fScaledLowerYValue
)/2.0;
218 rAlignment
= LABEL_ALIGN_CENTER
;
219 OSL_FAIL( "right label placement is not supported by column charts" );
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
;
231 rAlignment
= bNormalOutside
? LABEL_ALIGN_RIGHT
: LABEL_ALIGN_LEFT
;
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
;
237 rAlignment
= bNormalOutside
? LABEL_ALIGN_TOP
: LABEL_ALIGN_BOTTOM
;
239 fDepth
= (fBaseValue
< fScaledUpperYValue
) ? fabs(fScaledUpperBarDepth
) : fabs(fScaledLowerBarDepth
);
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
;
248 rAlignment
= bNormalOutside
? LABEL_ALIGN_BOTTOM
: LABEL_ALIGN_TOP
;
250 fDepth
= (fBaseValue
< fScaledUpperYValue
) ? fabs(fScaledUpperBarDepth
) : fabs(fScaledLowerBarDepth
);
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
;
261 rAlignment
= bNormalOutside
? LABEL_ALIGN_RIGHT
: LABEL_ALIGN_LEFT
;
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
;
267 rAlignment
= bNormalOutside
? LABEL_ALIGN_TOP
: LABEL_ALIGN_BOTTOM
;
269 fDepth
= (fBaseValue
< fScaledUpperYValue
) ? fabs(fScaledLowerBarDepth
) : fabs(fScaledUpperBarDepth
);
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
;
279 rAlignment
= LABEL_ALIGN_TOP
;
281 rAlignment
= LABEL_ALIGN_CENTER
;
283 fDepth
= fabs(fScaledUpperBarDepth
-fScaledLowerBarDepth
)/2.0;
286 OSL_FAIL("this label alignment is not implemented yet");
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
);
328 case DataPointGeometry3D::CONE
:
329 xShape
= m_pShapeFactory
->createCone( xTarget
, rPosition
, rSize
, fTopHeight
, nRotateZAngleHundredthDegree
);
331 case DataPointGeometry3D::PYRAMID
:
332 xShape
= m_pShapeFactory
->createPyramid( xTarget
, rPosition
, rSize
, fTopHeight
, nRotateZAngleHundredthDegree
>0
333 , xObjectProperties
, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
335 case DataPointGeometry3D::CUBOID
:
337 xShape
= m_pShapeFactory
->createCube( xTarget
, rPosition
, rSize
338 , nRotateZAngleHundredthDegree
, xObjectProperties
339 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), bRoundedEdges
);
342 if( nGeometry3D
!= DataPointGeometry3D::PYRAMID
)
343 setMappedProperties( xShape
, xObjectProperties
, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
349 bool lcl_hasGeometry3DVariableWidth( sal_Int32 nGeometry3D
)
352 switch( nGeometry3D
)
354 case DataPointGeometry3D::PYRAMID
:
355 case DataPointGeometry3D::CONE
:
358 case DataPointGeometry3D::CUBOID
:
359 case DataPointGeometry3D::CYLINDER
:
366 }// end anonymous namespace
368 void BarChart::addSeries( std::unique_ptr
<VDataSeries
> pSeries
, sal_Int32 zSlot
, sal_Int32 xSlot
, sal_Int32 ySlot
)
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();
380 if( !pSeries
->getGroupBarsPerAxis() )
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
);
390 //better performance for big data
391 struct FormerBarPoint
393 FormerBarPoint( double fX
, double fUpperY
, double fLowerY
, double fZ
)
394 : m_fX(fX
), m_fUpperY(fUpperY
), m_fLowerY(fLowerY
), m_fZ(fZ
)
398 ::rtl::math::setNan( &m_fX
);
399 ::rtl::math::setNan( &m_fUpperY
);
400 ::rtl::math::setNan( &m_fLowerY
);
401 ::rtl::math::setNan( &m_fZ
);
412 void BarChart::adaptOverlapAndGapwidthForGroupBarsPerAxis()
414 //adapt m_aOverlapSequence and m_aGapwidthSequence for the groupBarsPerAxis feature
415 //thus the different series use the same settings
417 VDataSeries
* pFirstSeries
= getFirstSeries();
418 if(!pFirstSeries
|| pFirstSeries
->getGroupBarsPerAxis())
421 sal_Int32 nAxisIndex
= pFirstSeries
->getAttachedAxisIndex();
423 sal_Int32 nUseThisIndex
= nAxisIndex
;
424 if( nUseThisIndex
< 0 || nUseThisIndex
>= m_aOverlapSequence
.getLength() )
426 for( nN
= 0; nN
< m_aOverlapSequence
.getLength(); nN
++ )
428 if(nN
!=nUseThisIndex
)
429 m_aOverlapSequence
[nN
] = m_aOverlapSequence
[nUseThisIndex
];
432 nUseThisIndex
= nAxisIndex
;
433 if( nUseThisIndex
< 0 || nUseThisIndex
>= m_aGapwidthSequence
.getLength() )
435 for( nN
= 0; nN
< m_aGapwidthSequence
.getLength(); nN
++ )
437 if(nN
!=nUseThisIndex
)
438 m_aGapwidthSequence
[nN
] = m_aGapwidthSequence
[nUseThisIndex
];
442 static E3dScene
* lcl_getE3dScene(uno::Reference
<uno::XInterface
> const & xInterface
)
444 E3dScene
* pScene
= nullptr;
446 SvxShape
* pSvxShape
= comphelper::getUnoTunnelImplementation
<SvxShape
>(xInterface
);
449 SdrObject
* pObject
= pSvxShape
->GetSdrObject();
450 pScene
= dynamic_cast<E3dScene
*>(pObject
);
455 void BarChart::createShapes()
457 if( m_aZSlots
.empty() ) //no series
460 OSL_ENSURE(m_pShapeFactory
&&m_xLogicTarget
.is()&&m_xFinalTarget
.is(),"BarChart is not proper initialized");
461 if(!(m_pShapeFactory
&&m_xLogicTarget
.is()&&m_xFinalTarget
.is()))
464 //the text labels should be always on top of the other series shapes
465 //therefore create an own group for the texts to move them to front
466 //(because the text group is created after the series group the texts are displayed on top)
468 //the regression curves should always be on top of the bars but beneath the text labels
469 //to achieve this the regression curve target is created after the series target and before the text target
471 uno::Reference
< drawing::XShapes
> xSeriesTarget(
472 createGroupShape( m_xLogicTarget
));
473 uno::Reference
< drawing::XShapes
> xRegressionCurveTarget(
474 createGroupShape( m_xLogicTarget
));
475 uno::Reference
< drawing::XShapes
> xTextTarget(
476 m_pShapeFactory
->createGroup2D( m_xFinalTarget
));
478 uno::Reference
< drawing::XShapes
> xRegressionCurveEquationTarget(
479 m_pShapeFactory
->createGroup2D( m_xFinalTarget
));
480 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
482 double fLogicZ
= 1.0;//as defined
484 bool bDrawConnectionLines
= false;
485 bool bDrawConnectionLinesInited
= false;
486 bool bOnlyConnectionLinesForThisPoint
= false;
488 std::unordered_set
<uno::Reference
<drawing::XShape
>> aShapeSet
;
490 const comphelper::ScopeGuard
aGuard([aShapeSet
]() {
492 std::unordered_set
<E3dScene
*> aSceneSet
;
494 for (uno::Reference
<drawing::XShape
> const & rShape
: aShapeSet
)
496 E3dScene
* pScene
= lcl_getE3dScene(rShape
);
497 if(nullptr != pScene
)
499 aSceneSet
.insert(pScene
->getRootE3dSceneFromE3dObject());
502 for (E3dScene
* pScene
: aSceneSet
)
504 pScene
->ResumeReportingDirtyRects();
505 pScene
->SetAllSceneRectsDirty();
509 adaptOverlapAndGapwidthForGroupBarsPerAxis();
511 //better performance for big data
512 std::map
< VDataSeries
*, FormerBarPoint
> aSeriesFormerPointMap
;
513 m_bPointsWereSkipped
= false;
514 sal_Int32 nSkippedPoints
= 0;
515 sal_Int32 nCreatedPoints
= 0;
517 sal_Int32 nStartIndex
= 0;
518 sal_Int32 nEndIndex
= VSeriesPlotter::getPointCount();
519 //iterate through all x values per indices
520 for( sal_Int32 nPointIndex
= nStartIndex
; nPointIndex
< nEndIndex
; nPointIndex
++ )
522 //sum up the values for all series in a complete z slot per attached axis
523 std::map
< sal_Int32
, double > aLogicYSumMap
;
524 for( auto& rZSlot
: m_aZSlots
)
526 for( auto& rXSlot
: rZSlot
)
528 sal_Int32 nAttachedAxisIndex
= rXSlot
.getAttachedAxisIndexForFirstSeries();
529 if( aLogicYSumMap
.find(nAttachedAxisIndex
)==aLogicYSumMap
.end() )
530 aLogicYSumMap
[nAttachedAxisIndex
]=0.0;
532 const sal_Int32 nSlotPoints
= rXSlot
.getPointCount();
533 if( nPointIndex
>= nSlotPoints
)
536 double fMinimumY
= 0.0, fMaximumY
= 0.0;
537 rXSlot
.calculateYMinAndMaxForCategory( nPointIndex
538 , isSeparateStackingForDifferentSigns( 1 ), fMinimumY
, fMaximumY
, nAttachedAxisIndex
);
540 if( !std::isnan( fMaximumY
) && fMaximumY
> 0)
541 aLogicYSumMap
[nAttachedAxisIndex
] += fMaximumY
;
542 if( !std::isnan( fMinimumY
) && fMinimumY
< 0)
543 aLogicYSumMap
[nAttachedAxisIndex
] += fabs(fMinimumY
);
548 for( auto& rZSlot
: m_aZSlots
)
550 //iterate through all x slots in this category
552 for( auto& rXSlot
: rZSlot
)
554 sal_Int32 nAttachedAxisIndex
= rXSlot
.getAttachedAxisIndexForFirstSeries();
555 //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot
556 BarPositionHelper
* pPosHelper
= dynamic_cast<BarPositionHelper
*>(&( getPlottingPositionHelper( nAttachedAxisIndex
) ) );
558 pPosHelper
= m_pMainPosHelper
.get();
560 PlotterBase::m_pPosHelper
= pPosHelper
;
562 //update/create information for current group
563 pPosHelper
->updateSeriesCount( rZSlot
.size() );
564 double fLogicBaseWidth
= pPosHelper
->getScaledSlotWidth();
566 // get distance from base value to maximum and minimum
568 double fMinimumY
= 0.0, fMaximumY
= 0.0;
569 if( nPointIndex
< rXSlot
.getPointCount())
570 rXSlot
.calculateYMinAndMaxForCategory( nPointIndex
571 , isSeparateStackingForDifferentSigns( 1 ), fMinimumY
, fMaximumY
, nAttachedAxisIndex
);
573 double fLogicPositiveYSum
= 0.0;
574 if( !std::isnan( fMaximumY
) )
575 fLogicPositiveYSum
= fMaximumY
;
577 double fLogicNegativeYSum
= 0.0;
578 if( !std::isnan( fMinimumY
) )
579 fLogicNegativeYSum
= fMinimumY
;
581 if( pPosHelper
->isPercentY() )
583 /* #i70395# fLogicPositiveYSum contains sum of all positive
584 values, if any, otherwise the highest negative value.
585 fLogicNegativeYSum contains sum of all negative values,
586 if any, otherwise the lowest positive value.
587 Afterwards, fLogicPositiveYSum will contain the maximum
588 (positive) value that is related to 100%. */
590 // do nothing if there are positive values only
591 if( fLogicNegativeYSum
< 0.0 )
593 // fLogicPositiveYSum<0 => negative values only, use absolute of negative sum
594 if( fLogicPositiveYSum
< 0.0 )
595 fLogicPositiveYSum
= -fLogicNegativeYSum
;
596 // otherwise there are positive and negative values, calculate total distance
598 fLogicPositiveYSum
-= fLogicNegativeYSum
;
600 fLogicNegativeYSum
= 0.0;
603 double fBaseValue
= 0.0;
604 if( !pPosHelper
->isPercentY() && rXSlot
.m_aSeriesVector
.size()<=1 )
605 fBaseValue
= pPosHelper
->getBaseValueY();
606 double fPositiveLogicYForNextSeries
= fBaseValue
;
607 double fNegativeLogicYForNextSeries
= fBaseValue
;
609 //iterate through all series in this x slot
610 for( std::unique_ptr
<VDataSeries
> const & pSeries
: rXSlot
.m_aSeriesVector
)
615 bool bHasFillColorMapping
= pSeries
->hasPropertyMapping("FillColor");
617 bOnlyConnectionLinesForThisPoint
= false;
619 if(nPointIndex
==nStartIndex
)//do not create a regression line for each point
620 createRegressionCurvesShapes( *pSeries
, xRegressionCurveTarget
, xRegressionCurveEquationTarget
,
621 m_pPosHelper
->maySkipPointsInRegressionCalculation());
623 if( !bDrawConnectionLinesInited
)
625 bDrawConnectionLines
= pSeries
->getConnectBars();
626 if( m_nDimension
==3 )
627 bDrawConnectionLines
= false;
628 if( bDrawConnectionLines
&& rXSlot
.m_aSeriesVector
.size()==1 )
630 //detect whether we have a stacked chart or not:
631 StackingDirection eDirection
= pSeries
->getStackingDirection();
632 if( eDirection
!= StackingDirection_Y_STACKING
)
633 bDrawConnectionLines
= false;
635 bDrawConnectionLinesInited
= true;
638 uno::Reference
<drawing::XShapes
> xSeriesGroupShape_Shapes(getSeriesGroupShape(pSeries
.get(), xSeriesTarget
));
639 uno::Reference
<drawing::XShape
> xSeriesGroupShape(xSeriesGroupShape_Shapes
, uno::UNO_QUERY
);
640 // Suspend setting rects dirty for the duration of this call
641 aShapeSet
.insert(xSeriesGroupShape
);
642 E3dScene
* pScene
= lcl_getE3dScene(xSeriesGroupShape
);
644 pScene
->SuspendReportingDirtyRects();
646 //collect data point information (logic coordinates, style ):
647 double fUnscaledLogicX
= pSeries
->getXValue( nPointIndex
);
648 fUnscaledLogicX
= DateHelper::RasterizeDateValue( fUnscaledLogicX
, m_aNullDate
, m_nTimeResolution
);
649 if(std::isnan(fUnscaledLogicX
))
650 continue;//point not visible
651 if(fUnscaledLogicX
<pPosHelper
->getLogicMinX())
652 continue;//point not visible
653 if(fUnscaledLogicX
>pPosHelper
->getLogicMaxX())
654 continue;//point not visible
655 if(pPosHelper
->isStrongLowerRequested(0) && fUnscaledLogicX
==pPosHelper
->getLogicMaxX())
656 continue;//point not visible
657 double fLogicX
= pPosHelper
->getScaledSlotPos( fUnscaledLogicX
, fSlotX
);
659 double fLogicBarHeight
= pSeries
->getYValue( nPointIndex
);
660 if( std::isnan( fLogicBarHeight
)) //no value at this category
663 double fLogicValueForLabeDisplay
= fLogicBarHeight
;
664 fLogicBarHeight
-=fBaseValue
;
666 if( pPosHelper
->isPercentY() )
668 if(fLogicPositiveYSum
!=0.0)
669 fLogicBarHeight
= fabs( fLogicBarHeight
)/fLogicPositiveYSum
;
671 fLogicBarHeight
= 0.0;
674 // tdf#114141 to draw the top of the zero height 3D bar
675 // we set a small positive value, here the smallest one for the type double (DBL_MIN)
676 if( fLogicBarHeight
== 0.0 )
677 fLogicBarHeight
= DBL_MIN
;
679 //sort negative and positive values, to display them on different sides of the x axis
680 bool bPositive
= fLogicBarHeight
>= 0.0;
681 double fLowerYValue
= bPositive
? fPositiveLogicYForNextSeries
: fNegativeLogicYForNextSeries
;
682 double fUpperYValue
= fLowerYValue
+fLogicBarHeight
;
684 fPositiveLogicYForNextSeries
+= fLogicBarHeight
;
686 fNegativeLogicYForNextSeries
+= fLogicBarHeight
;
691 drawing::Position3D
aUnscaledLogicPosition( fUnscaledLogicX
, fUpperYValue
, fLogicZ
);
693 //@todo ... start an iteration over the different breaks of the axis
694 //each subsystem may add an additional shape to form the whole point
695 //create a group shape for this point and add to the series shape:
696 // uno::Reference< drawing::XShapes > xPointGroupShape_Shapes( createGroupShape(xSeriesGroupShape_Shapes) );
697 // uno::Reference<drawing::XShape> xPointGroupShape_Shape =
698 // uno::Reference<drawing::XShape>( xPointGroupShape_Shapes, uno::UNO_QUERY );
699 //as long as we do not iterate we do not need to create an additional group for each point
700 uno::Reference
< beans::XPropertySet
> xDataPointProperties( pSeries
->getPropertiesOfPoint( nPointIndex
) );
701 sal_Int32 nGeometry3D
= DataPointGeometry3D::CUBOID
;
702 if(m_nDimension
==3) try
704 xDataPointProperties
->getPropertyValue( "Geometry3D") >>= nGeometry3D
;
706 catch( const uno::Exception
& )
708 TOOLS_WARN_EXCEPTION("chart2", "" );
711 //@todo iterate through all subsystems to create partial points
713 //@todo select a suitable PositionHelper for this subsystem
714 BarPositionHelper
* pSubPosHelper
= pPosHelper
;
716 double fUnclippedUpperYValue
= fUpperYValue
;
718 //apply clipping to Y
719 if( !pPosHelper
->clipYRange(fLowerYValue
,fUpperYValue
) )
721 if( bDrawConnectionLines
)
722 bOnlyConnectionLinesForThisPoint
= true;
726 //@todo clipping of X and Z is not fully integrated so far, as there is a need to create different objects
728 //apply scaling to Y before calculating width (necessary to maintain gradient in clipped objects)
729 pSubPosHelper
->doLogicScaling(nullptr,&fLowerYValue
,nullptr);
730 pSubPosHelper
->doLogicScaling(nullptr,&fUpperYValue
,nullptr);
731 //scaling of X and Z is not provided as the created objects should be symmetric in that dimensions
733 pSubPosHelper
->doLogicScaling(nullptr,&fUnclippedUpperYValue
,nullptr);
735 //calculate resulting width
736 double fCompleteHeight
= bPositive
? fLogicPositiveYSum
: fLogicNegativeYSum
;
737 if( pPosHelper
->isPercentY() )
738 fCompleteHeight
= 1.0;
739 double fLogicBarWidth
= fLogicBaseWidth
;
740 double fTopHeight
=approxSub(fCompleteHeight
,fUpperYValue
);
742 fTopHeight
=approxSub(fCompleteHeight
,fLowerYValue
);
743 double fLogicYStart
= bPositive
? fLowerYValue
: fUpperYValue
;
744 double fMiddleHeight
= fUpperYValue
-fLowerYValue
;
747 double fLogicBarDepth
= 0.5;
750 if( lcl_hasGeometry3DVariableWidth(nGeometry3D
) && fCompleteHeight
!=0.0 )
752 double fHeight
= fCompleteHeight
-fLowerYValue
;
754 fHeight
= fCompleteHeight
-fUpperYValue
;
755 fLogicBarWidth
= fLogicBaseWidth
*fHeight
/fCompleteHeight
;
756 if(fLogicBarWidth
<=0.0)
757 fLogicBarWidth
=fLogicBaseWidth
;
758 fLogicBarDepth
= fLogicBarDepth
*fHeight
/fCompleteHeight
;
759 if(fLogicBarDepth
<=0.0)
760 fLogicBarDepth
*=-1.0;
764 //better performance for big data
765 FormerBarPoint
aFormerPoint( aSeriesFormerPointMap
[pSeries
.get()] );
766 pPosHelper
->setCoordinateSystemResolution( m_aCoordinateSystemResolution
);
767 if( !pSeries
->isAttributedDataPoint(nPointIndex
)
769 pPosHelper
->isSameForGivenResolution( aFormerPoint
.m_fX
, aFormerPoint
.m_fUpperY
, aFormerPoint
.m_fZ
770 , fLogicX
, fUpperYValue
, fLogicZ
)
772 pPosHelper
->isSameForGivenResolution( aFormerPoint
.m_fX
, aFormerPoint
.m_fLowerY
, aFormerPoint
.m_fZ
773 , fLogicX
, fLowerYValue
, fLogicZ
)
777 m_bPointsWereSkipped
= true;
780 aSeriesFormerPointMap
[pSeries
.get()] = FormerBarPoint(fLogicX
,fUpperYValue
,fLowerYValue
,fLogicZ
);
782 if( bDrawConnectionLines
)
784 //store point information for connection lines
786 drawing::Position3D
aLeftUpperPoint( fLogicX
-fLogicBarWidth
/2.0,fUnclippedUpperYValue
,fLogicZ
);
787 drawing::Position3D
aRightUpperPoint( fLogicX
+fLogicBarWidth
/2.0,fUnclippedUpperYValue
,fLogicZ
);
789 if( isValidPosition(aLeftUpperPoint
) )
790 AddPointToPoly( pSeries
->m_aPolyPolygonShape3D
, aLeftUpperPoint
);
791 if( isValidPosition(aRightUpperPoint
) )
792 AddPointToPoly( pSeries
->m_aPolyPolygonShape3D
, aRightUpperPoint
);
795 if( bOnlyConnectionLinesForThisPoint
)
798 //maybe additional possibility for performance improvement
799 //bool bCreateLineInsteadOfComplexGeometryDueToMissingSpace = false;
800 //pPosHelper->isSameForGivenResolution( fLogicX-fLogicBarWidth/2.0, fLowerYValue, fLogicZ
801 // , fLogicX+fLogicBarWidth/2.0, fLowerYValue, fLogicZ );
804 //create partial point
805 if( !approxEqual(fLowerYValue
,fUpperYValue
) )
807 uno::Reference
< drawing::XShape
> xShape
;
808 if( m_nDimension
==3 )
810 drawing::Position3D
aLogicBottom (fLogicX
,fLogicYStart
,fLogicZ
);
811 drawing::Position3D
aLogicLeftBottomFront (fLogicX
+fLogicBarWidth
/2.0,fLogicYStart
,fLogicZ
-fLogicBarDepth
/2.0);
812 drawing::Position3D
aLogicRightDeepTop (fLogicX
-fLogicBarWidth
/2.0,fLogicYStart
+fMiddleHeight
,fLogicZ
+fLogicBarDepth
/2.0);
813 drawing::Position3D
aLogicTopTop (fLogicX
,fLogicYStart
+fMiddleHeight
+fTopHeight
,fLogicZ
);
815 uno::Reference
< XTransformation
> xTransformation
= pSubPosHelper
->getTransformationScaledLogicToScene();
817 //transformation 3) -> 4)
818 drawing::Position3D
aTransformedBottom ( SequenceToPosition3D( xTransformation
->transform( Position3DToSequence(aLogicBottom
) ) ) );
819 drawing::Position3D
aTransformedLeftBottomFront ( SequenceToPosition3D( xTransformation
->transform( Position3DToSequence(aLogicLeftBottomFront
) ) ) );
820 drawing::Position3D
aTransformedRightDeepTop ( SequenceToPosition3D( xTransformation
->transform( Position3DToSequence(aLogicRightDeepTop
) ) ) );
821 drawing::Position3D
aTransformedTopTop ( SequenceToPosition3D( xTransformation
->transform( Position3DToSequence(aLogicTopTop
) ) ) );
823 drawing::Direction3D aSize
= aTransformedRightDeepTop
- aTransformedLeftBottomFront
;
824 drawing::Direction3D
aTopSize( aTransformedTopTop
- aTransformedRightDeepTop
);
825 fTopHeight
= aTopSize
.DirectionY
;
827 sal_Int32 nRotateZAngleHundredthDegree
= 0;
828 if( pPosHelper
->isSwapXAndY() )
830 fTopHeight
= aTopSize
.DirectionX
;
831 nRotateZAngleHundredthDegree
= 90*100;
832 aSize
= drawing::Direction3D(aSize
.DirectionY
,aSize
.DirectionX
,aSize
.DirectionZ
);
835 if( aSize
.DirectionX
< 0 )
836 aSize
.DirectionX
*= -1.0;
837 if( aSize
.DirectionZ
< 0 )
838 aSize
.DirectionZ
*= -1.0;
842 xShape
= createDataPoint3D_Bar(
843 xSeriesGroupShape_Shapes
, aTransformedBottom
, aSize
, fTopHeight
, nRotateZAngleHundredthDegree
844 , xDataPointProperties
, nGeometry3D
);
846 else //m_nDimension!=3
848 // performance improvement: alloc the sequence before the rendering
849 // otherwise we have 2 realloc calls
850 drawing::PolyPolygonShape3D aPoly
;
851 aPoly
.SequenceX
.realloc(1);
852 aPoly
.SequenceY
.realloc(1);
853 aPoly
.SequenceZ
.realloc(1);
854 drawing::Position3D
aLeftUpperPoint( fLogicX
-fLogicBarWidth
/2.0,fUpperYValue
,fLogicZ
);
855 drawing::Position3D
aRightUpperPoint( fLogicX
+fLogicBarWidth
/2.0,fUpperYValue
,fLogicZ
);
857 AddPointToPoly( aPoly
, drawing::Position3D( fLogicX
-fLogicBarWidth
/2.0,fLowerYValue
,fLogicZ
) );
858 AddPointToPoly( aPoly
, drawing::Position3D( fLogicX
+fLogicBarWidth
/2.0,fLowerYValue
,fLogicZ
) );
859 AddPointToPoly( aPoly
, aRightUpperPoint
);
860 AddPointToPoly( aPoly
, aLeftUpperPoint
);
861 AddPointToPoly( aPoly
, drawing::Position3D( fLogicX
-fLogicBarWidth
/2.0,fLowerYValue
,fLogicZ
) );
862 pPosHelper
->transformScaledLogicToScene( aPoly
);
863 xShape
= m_pShapeFactory
->createArea2D( xSeriesGroupShape_Shapes
, aPoly
);
864 setMappedProperties( xShape
, xDataPointProperties
, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
867 if(bHasFillColorMapping
)
869 double nPropVal
= pSeries
->getValueByProperty(nPointIndex
, "FillColor");
870 if(!std::isnan(nPropVal
))
872 uno::Reference
< beans::XPropertySet
> xProps( xShape
, uno::UNO_QUERY_THROW
);
873 xProps
->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32
>(nPropVal
)));
876 //set name/classified ObjectID (CID)
877 ShapeFactory::setShapeName(xShape
878 , ObjectIdentifier::createPointCID(
879 pSeries
->getPointCID_Stub(),nPointIndex
) );
883 createErrorBar_Y( aUnscaledLogicPosition
, *pSeries
, nPointIndex
, m_xLogicTarget
, &fLogicX
);
885 //create data point label
886 if( pSeries
->getDataPointLabelIfLabel(nPointIndex
) )
888 double fLogicSum
= aLogicYSumMap
[nAttachedAxisIndex
];
890 LabelAlignment
eAlignment(LABEL_ALIGN_CENTER
);
891 sal_Int32 nLabelPlacement
= pSeries
->getLabelPlacement( nPointIndex
, m_xChartTypeModel
, pPosHelper
->isSwapXAndY() );
893 double fLowerBarDepth
= fLogicBarDepth
;
894 double fUpperBarDepth
= fLogicBarDepth
;
896 if( lcl_hasGeometry3DVariableWidth(nGeometry3D
) && fCompleteHeight
!=0.0 )
898 double fOuterBarDepth
= fLogicBarDepth
* fTopHeight
/(fabs(fCompleteHeight
));
899 fLowerBarDepth
= (fBaseValue
< fUpperYValue
) ? fabs(fLogicBarDepth
) : fabs(fOuterBarDepth
);
900 fUpperBarDepth
= (fBaseValue
< fUpperYValue
) ? fabs(fOuterBarDepth
) : fabs(fLogicBarDepth
);
904 awt::Point aScreenPosition2D
= getLabelScreenPositionAndAlignment(
905 eAlignment
, nLabelPlacement
, fLogicX
, fLowerYValue
, fUpperYValue
, fLogicZ
,
906 fLowerBarDepth
, fUpperBarDepth
, fBaseValue
, pPosHelper
);
907 sal_Int32 nOffset
= 0;
908 if(eAlignment
!=LABEL_ALIGN_CENTER
)
910 nOffset
= 100;//add some spacing //@todo maybe get more intelligent values
911 if( m_nDimension
== 3 )
915 xTextTarget
, *pSeries
, nPointIndex
,
916 fLogicValueForLabeDisplay
, fLogicSum
, aScreenPosition2D
, eAlignment
, nOffset
);
919 }//end iteration through partial points
921 }//next series in x slot (next y slot)
927 if( bDrawConnectionLines
)
929 for( auto const& rZSlot
: m_aZSlots
)
931 BarPositionHelper
* pPosHelper
= m_pMainPosHelper
.get();
932 if( !rZSlot
.empty() )
934 sal_Int32 nAttachedAxisIndex
= rZSlot
.front().getAttachedAxisIndexForFirstSeries();
935 //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot
936 pPosHelper
= dynamic_cast<BarPositionHelper
*>(&( getPlottingPositionHelper( nAttachedAxisIndex
) ) );
938 pPosHelper
= m_pMainPosHelper
.get();
940 PlotterBase::m_pPosHelper
= pPosHelper
;
942 //iterate through all x slots in this category
943 for( auto const& rXSlot
: rZSlot
)
945 //iterate through all series in this x slot
946 for( std::unique_ptr
<VDataSeries
> const & pSeries
: rXSlot
.m_aSeriesVector
)
950 drawing::PolyPolygonShape3D
* pSeriesPoly
= &pSeries
->m_aPolyPolygonShape3D
;
951 if(!ShapeFactory::hasPolygonAnyLines(*pSeriesPoly
))
954 drawing::PolyPolygonShape3D aPoly
;
955 Clipping::clipPolygonAtRectangle( *pSeriesPoly
, pPosHelper
->getScaledLogicClipDoubleRect(), aPoly
);
957 if(!ShapeFactory::hasPolygonAnyLines(aPoly
))
960 //transformation 3) -> 4)
961 pPosHelper
->transformScaledLogicToScene( aPoly
);
963 uno::Reference
< drawing::XShapes
> xSeriesGroupShape_Shapes(
964 getSeriesGroupShape(pSeries
.get(), xSeriesTarget
) );
965 uno::Reference
< drawing::XShape
> xShape( m_pShapeFactory
->createLine2D(
966 xSeriesGroupShape_Shapes
, PolyToPointSequence( aPoly
) ) );
967 setMappedProperties( xShape
, pSeries
->getPropertiesOfSeries()
968 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
974 /* @todo remove series shapes if empty
979 "skipped points: " << nSkippedPoints
<< " created points: "
985 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */