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
);
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
)
396 ::rtl::math::setNan( &m_fX
);
397 ::rtl::math::setNan( &m_fUpperY
);
398 ::rtl::math::setNan( &m_fLowerY
);
399 ::rtl::math::setNan( &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();
418 sal_Int32 nUseThisIndex
= nAxisIndex
;
419 if( nUseThisIndex
< 0 || nUseThisIndex
>= m_aOverlapSequence
.getLength() )
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() )
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
);
445 SdrObject
* pObject
= pSvxShape
->GetSdrObject();
446 pScene
= dynamic_cast<E3dScene
*>(pObject
);
451 void BarChart::createShapes()
453 if( m_aZSlots
.empty() ) //no series
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()))
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
)
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
);
544 for( auto& rZSlot
: m_aZSlots
)
546 //iterate through all x slots in this category
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
) ) );
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
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
)
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
);
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
659 double fLogicValueForLabeDisplay
= fLogicBarHeight
;
660 fLogicBarHeight
-=fBaseValue
;
662 if( pPosHelper
->isPercentY() )
664 if(fLogicPositiveYSum
!=0.0)
665 fLogicBarHeight
= fabs( fLogicBarHeight
)/fLogicPositiveYSum
;
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
;
680 fPositiveLogicYForNextSeries
+= fLogicBarHeight
;
682 fNegativeLogicYForNextSeries
+= fLogicBarHeight
;
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;
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
);
738 fTopHeight
=approxSub(fCompleteHeight
,fLowerYValue
);
739 double fLogicYStart
= bPositive
? fLowerYValue
: fUpperYValue
;
740 double fMiddleHeight
= fUpperYValue
-fLowerYValue
;
743 double fLogicBarDepth
= 0.5;
746 if( lcl_hasGeometry3DVariableWidth(nGeometry3D
) && fCompleteHeight
!=0.0 )
748 double fHeight
= fCompleteHeight
-fLowerYValue
;
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
)
773 m_bPointsWereSkipped
= true;
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
)
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 );
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;
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
) );
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 )
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)
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
) ) );
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
)
946 drawing::PolyPolygonShape3D
* pSeriesPoly
= &pSeries
->m_aPolyPolygonShape3D
;
947 if(!ShapeFactory::hasPolygonAnyLines(*pSeriesPoly
))
950 drawing::PolyPolygonShape3D aPoly
;
951 Clipping::clipPolygonAtRectangle( *pSeriesPoly
, pPosHelper
->getScaledLogicClipDoubleRect(), aPoly
);
953 if(!ShapeFactory::hasPolygonAnyLines(aPoly
))
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
975 "skipped points: " << nSkippedPoints
<< " created points: "
981 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */