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 "ShapeFactory.hxx"
22 #include "CommonConverters.hxx"
23 #include "ObjectIdentifier.hxx"
24 #include "LabelPositionHelper.hxx"
25 #include "BarPositionHelper.hxx"
27 #include "AxisIndexDefines.hxx"
28 #include "Clipping.hxx"
29 #include "DateHelper.hxx"
31 #include <com/sun/star/chart/DataLabelPlacement.hpp>
33 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
34 #include <rtl/math.hxx>
38 using namespace ::com::sun::star
;
39 using namespace ::rtl::math
;
40 using namespace ::com::sun::star::chart2
;
42 BarChart::BarChart( const uno::Reference
<XChartType
>& xChartTypeModel
43 , sal_Int32 nDimensionCount
)
44 : VSeriesPlotter( xChartTypeModel
, nDimensionCount
)
45 , m_pMainPosHelper( new BarPositionHelper() )
47 PlotterBase::m_pPosHelper
= m_pMainPosHelper
;
48 VSeriesPlotter::m_pMainPosHelper
= m_pMainPosHelper
;
52 if( m_xChartTypeModelProps
.is() )
54 m_xChartTypeModelProps
->getPropertyValue( "OverlapSequence" ) >>= m_aOverlapSequence
;
55 m_xChartTypeModelProps
->getPropertyValue( "GapwidthSequence" ) >>= m_aGapwidthSequence
;
58 catch( const uno::Exception
& e
)
60 ASSERT_EXCEPTION( e
);
66 delete m_pMainPosHelper
;
69 PlottingPositionHelper
& BarChart::getPlottingPositionHelper( sal_Int32 nAxisIndex
) const
71 PlottingPositionHelper
& rPosHelper
= VSeriesPlotter::getPlottingPositionHelper( nAxisIndex
);
72 BarPositionHelper
* pBarPosHelper
= dynamic_cast<BarPositionHelper
*>(&rPosHelper
);
73 if( pBarPosHelper
&& nAxisIndex
>= 0 )
75 if( nAxisIndex
< m_aOverlapSequence
.getLength() )
76 pBarPosHelper
->setInnerDistance( -m_aOverlapSequence
[nAxisIndex
]/100.0 );
77 if( nAxisIndex
< m_aGapwidthSequence
.getLength() )
78 pBarPosHelper
->setOuterDistance( m_aGapwidthSequence
[nAxisIndex
]/100.0 );
83 drawing::Direction3D
BarChart::getPreferredDiagramAspectRatio() const
85 drawing::Direction3D
aRet(1.0,1.0,1.0);
86 if( m_nDimension
== 3 )
88 aRet
= drawing::Direction3D(1.0,-1.0,1.0);
89 BarPositionHelper
* pPosHelper
= dynamic_cast<BarPositionHelper
*>(&( this->getPlottingPositionHelper( MAIN_AXIS_INDEX
) ) );
92 drawing::Direction3D
aScale( pPosHelper
->getScaledLogicWidth() );
93 if(aScale
.DirectionX
!=0.0)
95 double fXSlotCount
= 1.0;
96 if(!m_aZSlots
.empty())
98 fXSlotCount
= m_aZSlots
.begin()->size();
100 aRet
.DirectionZ
= aScale
.DirectionZ
/
101 (aScale
.DirectionX
+ aScale
.DirectionX
* (fXSlotCount
-1.0) * pPosHelper
->getScaledSlotWidth());
105 return VSeriesPlotter::getPreferredDiagramAspectRatio();
110 return VSeriesPlotter::getPreferredDiagramAspectRatio();
113 if(aRet
.DirectionZ
<0.05)
115 aRet
.DirectionZ
=0.05;
117 else if(aRet
.DirectionZ
>10)
121 if( m_pMainPosHelper
&& m_pMainPosHelper
->isSwapXAndY() )
123 double fTemp
= aRet
.DirectionX
;
124 aRet
.DirectionX
= aRet
.DirectionY
;
125 aRet
.DirectionY
= fTemp
;
129 aRet
= drawing::Direction3D(-1,-1,-1);
133 bool BarChart::keepAspectRatio() const
138 awt::Point
BarChart::getLabelScreenPositionAndAlignment(
139 LabelAlignment
& rAlignment
, sal_Int32 nLabelPlacement
140 , double fScaledX
, double fScaledLowerYValue
, double fScaledUpperYValue
, double fScaledZ
141 , double fScaledLowerBarDepth
, double fScaledUpperBarDepth
, double fBaseValue
142 , BarPositionHelper
* pPosHelper
145 double fX
= fScaledX
;
146 double fY
= fScaledUpperYValue
;
147 double fZ
= fScaledZ
;
148 bool bReverse
= !pPosHelper
->isMathematicalOrientationY();
149 bool bNormalOutside
= (!bReverse
== !!(fBaseValue
< fScaledUpperYValue
));
150 double fDepth
= fScaledUpperBarDepth
;
152 switch(nLabelPlacement
)
154 case ::com::sun::star::chart::DataLabelPlacement::TOP
:
156 if( !pPosHelper
->isSwapXAndY() )
158 fY
= bReverse
? fScaledLowerYValue
: fScaledUpperYValue
;
159 rAlignment
= LABEL_ALIGN_TOP
;
161 fDepth
= bReverse
? fabs(fScaledLowerBarDepth
) : fabs(fScaledUpperBarDepth
);
165 fY
-= (fScaledUpperYValue
-fScaledLowerYValue
)/2.0;
166 rAlignment
= LABEL_ALIGN_CENTER
;
167 OSL_FAIL( "top label placement is not really supported by horizontal bar charts" );
171 case ::com::sun::star::chart::DataLabelPlacement::BOTTOM
:
173 if(!pPosHelper
->isSwapXAndY())
175 fY
= bReverse
? fScaledUpperYValue
: fScaledLowerYValue
;
176 rAlignment
= LABEL_ALIGN_BOTTOM
;
178 fDepth
= bReverse
? fabs(fScaledUpperBarDepth
) : fabs(fScaledLowerBarDepth
);
182 fY
-= (fScaledUpperYValue
-fScaledLowerYValue
)/2.0;
183 rAlignment
= LABEL_ALIGN_CENTER
;
184 OSL_FAIL( "bottom label placement is not supported by horizontal bar charts" );
188 case ::com::sun::star::chart::DataLabelPlacement::LEFT
:
190 if( pPosHelper
->isSwapXAndY() )
192 fY
= bReverse
? fScaledUpperYValue
: fScaledLowerYValue
;
193 rAlignment
= LABEL_ALIGN_LEFT
;
195 fDepth
= bReverse
? fabs(fScaledUpperBarDepth
) : fabs(fScaledLowerBarDepth
);
199 fY
-= (fScaledUpperYValue
-fScaledLowerYValue
)/2.0;
200 rAlignment
= LABEL_ALIGN_CENTER
;
201 OSL_FAIL( "left label placement is not supported by column charts" );
205 case ::com::sun::star::chart::DataLabelPlacement::RIGHT
:
207 if( pPosHelper
->isSwapXAndY() )
209 fY
= bReverse
? fScaledLowerYValue
: fScaledUpperYValue
;
210 rAlignment
= LABEL_ALIGN_RIGHT
;
212 fDepth
= bReverse
? fabs(fScaledLowerBarDepth
) : fabs(fScaledUpperBarDepth
);
216 fY
-= (fScaledUpperYValue
-fScaledLowerYValue
)/2.0;
217 rAlignment
= LABEL_ALIGN_CENTER
;
218 OSL_FAIL( "right label placement is not supported by column charts" );
222 case ::com::sun::star::chart::DataLabelPlacement::OUTSIDE
:
224 fY
= (fBaseValue
< fScaledUpperYValue
) ? fScaledUpperYValue
: fScaledLowerYValue
;
225 if( pPosHelper
->isSwapXAndY() )
226 rAlignment
= bNormalOutside
? LABEL_ALIGN_RIGHT
: LABEL_ALIGN_LEFT
;
228 rAlignment
= bNormalOutside
? LABEL_ALIGN_TOP
: LABEL_ALIGN_BOTTOM
;
230 fDepth
= (fBaseValue
< fScaledUpperYValue
) ? fabs(fScaledUpperBarDepth
) : fabs(fScaledLowerBarDepth
);
233 case ::com::sun::star::chart::DataLabelPlacement::INSIDE
:
235 fY
= (fBaseValue
< fScaledUpperYValue
) ? fScaledUpperYValue
: fScaledLowerYValue
;
236 if( pPosHelper
->isSwapXAndY() )
237 rAlignment
= bNormalOutside
? LABEL_ALIGN_LEFT
: LABEL_ALIGN_RIGHT
;
239 rAlignment
= bNormalOutside
? LABEL_ALIGN_BOTTOM
: LABEL_ALIGN_TOP
;
241 fDepth
= (fBaseValue
< fScaledUpperYValue
) ? fabs(fScaledUpperBarDepth
) : fabs(fScaledLowerBarDepth
);
244 case ::com::sun::star::chart::DataLabelPlacement::NEAR_ORIGIN
:
246 fY
= (fBaseValue
< fScaledUpperYValue
) ? fScaledLowerYValue
: fScaledUpperYValue
;
247 if( pPosHelper
->isSwapXAndY() )
248 rAlignment
= bNormalOutside
? LABEL_ALIGN_RIGHT
: LABEL_ALIGN_LEFT
;
250 rAlignment
= bNormalOutside
? LABEL_ALIGN_TOP
: LABEL_ALIGN_BOTTOM
;
252 fDepth
= (fBaseValue
< fScaledUpperYValue
) ? fabs(fScaledLowerBarDepth
) : fabs(fScaledUpperBarDepth
);
255 case ::com::sun::star::chart::DataLabelPlacement::CENTER
:
256 fY
-= (fScaledUpperYValue
-fScaledLowerYValue
)/2.0;
257 rAlignment
= LABEL_ALIGN_CENTER
;
259 fDepth
= fabs(fScaledUpperBarDepth
-fScaledLowerBarDepth
)/2.0;
262 OSL_FAIL("this label alignment is not implemented yet");
269 drawing::Position3D
aScenePosition3D( pPosHelper
->
270 transformScaledLogicToScene( fX
, fY
, fZ
, true ) );
271 return LabelPositionHelper(pPosHelper
,m_nDimension
,m_xLogicTarget
,m_pShapeFactory
)
272 .transformSceneToScreenPosition( aScenePosition3D
);
275 uno::Reference
< drawing::XShape
> BarChart::createDataPoint3D_Bar(
276 const uno::Reference
< drawing::XShapes
>& xTarget
277 , const drawing::Position3D
& rPosition
, const drawing::Direction3D
& rSize
278 , double fTopHeight
, sal_Int32 nRotateZAngleHundredthDegree
279 , const uno::Reference
< beans::XPropertySet
>& xObjectProperties
280 , sal_Int32 nGeometry3D
)
282 bool bRoundedEdges
= true;
285 if( xObjectProperties
.is() )
287 sal_Int16 nPercentDiagonal
= 0;
288 xObjectProperties
->getPropertyValue( "PercentDiagonal" ) >>= nPercentDiagonal
;
289 if( nPercentDiagonal
< 5 )
290 bRoundedEdges
= false;
293 catch( const uno::Exception
& e
)
295 ASSERT_EXCEPTION( e
);
298 uno::Reference
< drawing::XShape
> xShape(NULL
);
299 switch( nGeometry3D
)
301 case DataPointGeometry3D::CYLINDER
:
302 xShape
= m_pShapeFactory
->createCylinder( xTarget
, rPosition
, rSize
, nRotateZAngleHundredthDegree
);
304 case DataPointGeometry3D::CONE
:
305 xShape
= m_pShapeFactory
->createCone( xTarget
, rPosition
, rSize
, fTopHeight
, nRotateZAngleHundredthDegree
);
307 case DataPointGeometry3D::PYRAMID
:
308 xShape
= m_pShapeFactory
->createPyramid( xTarget
, rPosition
, rSize
, fTopHeight
, nRotateZAngleHundredthDegree
>0
309 , xObjectProperties
, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
311 case DataPointGeometry3D::CUBOID
:
313 xShape
= m_pShapeFactory
->createCube( xTarget
, rPosition
, rSize
314 , nRotateZAngleHundredthDegree
, xObjectProperties
315 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), bRoundedEdges
);
318 if( nGeometry3D
!= DataPointGeometry3D::PYRAMID
)
319 setMappedProperties( xShape
, xObjectProperties
, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
325 bool lcl_hasGeometry3DVariableWidth( sal_Int32 nGeometry3D
)
328 switch( nGeometry3D
)
330 case DataPointGeometry3D::PYRAMID
:
331 case DataPointGeometry3D::CONE
:
334 case DataPointGeometry3D::CUBOID
:
335 case DataPointGeometry3D::CYLINDER
:
342 }// end anonymous namespace
344 void BarChart::addSeries( VDataSeries
* pSeries
, sal_Int32 zSlot
, sal_Int32 xSlot
, sal_Int32 ySlot
)
350 //2ND_AXIS_IN_BARS put series on second scales to different z slot as temporary workaround
351 //this needs to be redesigned if 3d bars are also able to display secondary axes
353 sal_Int32 nAxisIndex
= pSeries
->getAttachedAxisIndex();
356 if( !pSeries
->getGroupBarsPerAxis() )
358 if(zSlot
>=static_cast<sal_Int32
>(m_aZSlots
.size()))
359 m_aZSlots
.resize(zSlot
+1);
361 VSeriesPlotter::addSeries( pSeries
, zSlot
, xSlot
, ySlot
);
364 //better performance for big data
365 struct FormerBarPoint
367 FormerBarPoint( double fX
, double fUpperY
, double fLowerY
, double fZ
)
368 : m_fX(fX
), m_fUpperY(fUpperY
), m_fLowerY(fLowerY
), m_fZ(fZ
)
372 ::rtl::math::setNan( &m_fX
);
373 ::rtl::math::setNan( &m_fUpperY
);
374 ::rtl::math::setNan( &m_fLowerY
);
375 ::rtl::math::setNan( &m_fZ
);
384 void BarChart::adaptOverlapAndGapwidthForGroupBarsPerAxis()
386 //adapt m_aOverlapSequence and m_aGapwidthSequence for the groupBarsPerAxis feature
387 //thus the different series use the same settings
389 VDataSeries
* pFirstSeries
= getFirstSeries();
390 if(pFirstSeries
&& !pFirstSeries
->getGroupBarsPerAxis() )
392 sal_Int32 nAxisIndex
= pFirstSeries
->getAttachedAxisIndex();
394 sal_Int32 nUseThisIndex
= nAxisIndex
;
395 if( nUseThisIndex
< 0 || nUseThisIndex
>= m_aOverlapSequence
.getLength() )
397 for( nN
= 0; nN
< m_aOverlapSequence
.getLength(); nN
++ )
399 if(nN
!=nUseThisIndex
)
400 m_aOverlapSequence
[nN
] = m_aOverlapSequence
[nUseThisIndex
];
403 nUseThisIndex
= nAxisIndex
;
404 if( nUseThisIndex
< 0 || nUseThisIndex
>= m_aGapwidthSequence
.getLength() )
406 for( nN
= 0; nN
< m_aGapwidthSequence
.getLength(); nN
++ )
408 if(nN
!=nUseThisIndex
)
409 m_aGapwidthSequence
[nN
] = m_aGapwidthSequence
[nUseThisIndex
];
414 void BarChart::createShapes()
416 if( m_aZSlots
.begin() == m_aZSlots
.end() ) //no series
419 OSL_ENSURE(m_pShapeFactory
&&m_xLogicTarget
.is()&&m_xFinalTarget
.is(),"BarChart is not proper initialized");
420 if(!(m_pShapeFactory
&&m_xLogicTarget
.is()&&m_xFinalTarget
.is()))
423 //the text labels should be always on top of the other series shapes
424 //therefore create an own group for the texts to move them to front
425 //(because the text group is created after the series group the texts are displayed on top)
427 //the regression curves should always be on top of the bars but beneath the text labels
428 //to achieve this the regression curve target is created after the series target and before the text target
430 uno::Reference
< drawing::XShapes
> xSeriesTarget(
431 createGroupShape( m_xLogicTarget
,OUString() ));
432 uno::Reference
< drawing::XShapes
> xRegressionCurveTarget(
433 createGroupShape( m_xLogicTarget
,OUString() ));
434 uno::Reference
< drawing::XShapes
> xTextTarget(
435 m_pShapeFactory
->createGroup2D( m_xFinalTarget
,OUString() ));
437 uno::Reference
< drawing::XShapes
> xRegressionCurveEquationTarget(
438 m_pShapeFactory
->createGroup2D( m_xFinalTarget
,OUString() ));
439 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
441 double fLogicZ
= 1.0;//as defined
443 bool bDrawConnectionLines
= false;
444 bool bDrawConnectionLinesInited
= false;
445 bool bOnlyConnectionLinesForThisPoint
= false;
447 adaptOverlapAndGapwidthForGroupBarsPerAxis();
449 //better performance for big data
450 std::map
< VDataSeries
*, FormerBarPoint
> aSeriesFormerPointMap
;
451 m_bPointsWereSkipped
= false;
452 sal_Int32 nSkippedPoints
= 0;
453 sal_Int32 nCreatedPoints
= 0;
455 sal_Int32 nStartIndex
= 0;
456 sal_Int32 nEndIndex
= VSeriesPlotter::getPointCount();
457 //iterate through all x values per indices
458 for( sal_Int32 nPointIndex
= nStartIndex
; nPointIndex
< nEndIndex
; nPointIndex
++ )
460 ::std::vector
< ::std::vector
< VDataSeriesGroup
> >::iterator aZSlotIter
= m_aZSlots
.begin();
461 const ::std::vector
< ::std::vector
< VDataSeriesGroup
> >::const_iterator aZSlotEnd
= m_aZSlots
.end();
463 //sum up the values for all series in a complete z zlot per attached axis
464 ::std::map
< sal_Int32
, double > aLogicYSumMap
;
465 for( ; aZSlotIter
!= aZSlotEnd
; ++aZSlotIter
)
467 ::std::vector
< VDataSeriesGroup
>::iterator aXSlotIter
= aZSlotIter
->begin();
468 const ::std::vector
< VDataSeriesGroup
>::const_iterator aXSlotEnd
= aZSlotIter
->end();
470 for( aXSlotIter
= aZSlotIter
->begin(); aXSlotIter
!= aXSlotEnd
; ++aXSlotIter
)
472 sal_Int32 nAttachedAxisIndex
= aXSlotIter
->getAttachedAxisIndexForFirstSeries();
473 if( aLogicYSumMap
.find(nAttachedAxisIndex
)==aLogicYSumMap
.end() )
474 aLogicYSumMap
[nAttachedAxisIndex
]=0.0;
476 double fMinimumY
= 0.0, fMaximumY
= 0.0;
477 aXSlotIter
->calculateYMinAndMaxForCategory( nPointIndex
478 , isSeparateStackingForDifferentSigns( 1 ), fMinimumY
, fMaximumY
, nAttachedAxisIndex
);
480 if( !::rtl::math::isNan( fMaximumY
) && fMaximumY
> 0)
481 aLogicYSumMap
[nAttachedAxisIndex
] += fMaximumY
;
482 if( !::rtl::math::isNan( fMinimumY
) && fMinimumY
< 0)
483 aLogicYSumMap
[nAttachedAxisIndex
] += fabs(fMinimumY
);
487 aZSlotIter
= m_aZSlots
.begin();
488 for( sal_Int32 nZ
=1; aZSlotIter
!= aZSlotEnd
; ++aZSlotIter
, nZ
++ )
490 ::std::vector
< VDataSeriesGroup
>::iterator aXSlotIter
= aZSlotIter
->begin();
491 const ::std::vector
< VDataSeriesGroup
>::const_iterator aXSlotEnd
= aZSlotIter
->end();
493 //iterate through all x slots in this category
495 for( aXSlotIter
= aZSlotIter
->begin(); aXSlotIter
!= aXSlotEnd
; ++aXSlotIter
, fSlotX
+=1.0 )
497 sal_Int32 nAttachedAxisIndex
= 0;
498 BarPositionHelper
* pPosHelper
= m_pMainPosHelper
;
499 if( aXSlotIter
!= aXSlotEnd
)
501 nAttachedAxisIndex
= aXSlotIter
->getAttachedAxisIndexForFirstSeries();
502 //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot
503 pPosHelper
= dynamic_cast<BarPositionHelper
*>(&( this->getPlottingPositionHelper( nAttachedAxisIndex
) ) );
505 pPosHelper
= m_pMainPosHelper
;
507 PlotterBase::m_pPosHelper
= pPosHelper
;
509 //update/create information for current group
510 pPosHelper
->updateSeriesCount( aZSlotIter
->size() );
511 double fLogicBaseWidth
= pPosHelper
->getScaledSlotWidth();
513 ::std::vector
< VDataSeries
* >* pSeriesList
= &(aXSlotIter
->m_aSeriesVector
);
515 // get distance from base value to maximum and minimum
517 double fMinimumY
= 0.0, fMaximumY
= 0.0;
518 aXSlotIter
->calculateYMinAndMaxForCategory( nPointIndex
519 , isSeparateStackingForDifferentSigns( 1 ), fMinimumY
, fMaximumY
, nAttachedAxisIndex
);
521 double fLogicPositiveYSum
= 0.0;
522 if( !::rtl::math::isNan( fMaximumY
) )
523 fLogicPositiveYSum
= fMaximumY
;
525 double fLogicNegativeYSum
= 0.0;
526 if( !::rtl::math::isNan( fMinimumY
) )
527 fLogicNegativeYSum
= fMinimumY
;
529 if( pPosHelper
->isPercentY() )
531 /* #i70395# fLogicPositiveYSum contains sum of all positive
532 values, if any, otherwise the highest negative value.
533 fLogicNegativeYSum contains sum of all negative values,
534 if any, otherwise the lowest positive value.
535 Afterwards, fLogicPositiveYSum will contain the maximum
536 (positive) value that is related to 100%. */
538 // do nothing if there are positive values only
539 if( fLogicNegativeYSum
< 0.0 )
541 // fLogicPositiveYSum<0 => negative values only, use absolute of negative sum
542 if( fLogicPositiveYSum
< 0.0 )
543 fLogicPositiveYSum
= -fLogicNegativeYSum
;
544 // otherwise there are positive and negative values, calculate total distance
546 fLogicPositiveYSum
-= fLogicNegativeYSum
;
548 fLogicNegativeYSum
= 0.0;
551 double fBaseValue
= 0.0;
552 if( !pPosHelper
->isPercentY() && pSeriesList
->size()<=1 )
553 fBaseValue
= pPosHelper
->getBaseValueY();
554 double fPositiveLogicYForNextSeries
= fBaseValue
;
555 double fNegativeLogicYForNextSeries
= fBaseValue
;
557 ::std::vector
< VDataSeries
* >::const_iterator aSeriesIter
= pSeriesList
->begin();
558 const ::std::vector
< VDataSeries
* >::const_iterator aSeriesEnd
= pSeriesList
->end();
559 //iterate through all series in this x slot
560 for( ; aSeriesIter
!= aSeriesEnd
; ++aSeriesIter
)
562 VDataSeries
* pSeries( *aSeriesIter
);
566 bool bHasFillColorMapping
= pSeries
->hasPropertyMapping("FillColor");
568 bOnlyConnectionLinesForThisPoint
= false;
570 if(nPointIndex
==nStartIndex
)//do not create a regression line for each point
571 createRegressionCurvesShapes( **aSeriesIter
, xRegressionCurveTarget
, xRegressionCurveEquationTarget
,
572 m_pPosHelper
->maySkipPointsInRegressionCalculation());
574 if( !bDrawConnectionLinesInited
)
576 bDrawConnectionLines
= pSeries
->getConnectBars();
577 if( m_nDimension
==3 )
578 bDrawConnectionLines
= false;
579 if( bDrawConnectionLines
&& pSeriesList
->size()==1 )
581 //detect whether we have a stacked chart or not:
582 StackingDirection eDirection
= pSeries
->getStackingDirection();
583 if( eDirection
!= StackingDirection_Y_STACKING
)
584 bDrawConnectionLines
= false;
586 bDrawConnectionLinesInited
= true;
589 uno::Reference
< drawing::XShapes
> xSeriesGroupShape_Shapes(
590 getSeriesGroupShape(*aSeriesIter
, xSeriesTarget
) );
592 //collect data point information (logic coordinates, style ):
593 double fUnscaledLogicX
= (*aSeriesIter
)->getXValue( nPointIndex
);
594 fUnscaledLogicX
= DateHelper::RasterizeDateValue( fUnscaledLogicX
, m_aNullDate
, m_nTimeResolution
);
595 if(fUnscaledLogicX
<pPosHelper
->getLogicMinX())
596 continue;//point not visible
597 if(fUnscaledLogicX
>pPosHelper
->getLogicMaxX())
598 continue;//point not visible
599 if(pPosHelper
->isStrongLowerRequested(0) && fUnscaledLogicX
==pPosHelper
->getLogicMaxX())
600 continue;//point not visible
601 double fLogicX
= pPosHelper
->getScaledSlotPos( fUnscaledLogicX
, fSlotX
);
603 double fLogicBarHeight
= (*aSeriesIter
)->getYValue( nPointIndex
);
604 if( ::rtl::math::isNan( fLogicBarHeight
)) //no value at this category
607 double fLogicValueForLabeDisplay
= fLogicBarHeight
;
608 fLogicBarHeight
-=fBaseValue
;
610 if( pPosHelper
->isPercentY() )
612 if(fLogicPositiveYSum
!=0.0)
613 fLogicBarHeight
= fabs( fLogicBarHeight
)/fLogicPositiveYSum
;
615 fLogicBarHeight
= 0.0;
618 //sort negative and positive values, to display them on different sides of the x axis
619 bool bPositive
= fLogicBarHeight
>= 0.0;
620 double fLowerYValue
= bPositive
? fPositiveLogicYForNextSeries
: fNegativeLogicYForNextSeries
;
621 double fUpperYValue
= fLowerYValue
+fLogicBarHeight
;
623 fPositiveLogicYForNextSeries
+= fLogicBarHeight
;
625 fNegativeLogicYForNextSeries
+= fLogicBarHeight
;
630 drawing::Position3D
aUnscaledLogicPosition( fUnscaledLogicX
, fUpperYValue
, fLogicZ
);
632 //@todo ... start an iteration over the different breaks of the axis
633 //each subsystem may add an additional shape to form the whole point
634 //create a group shape for this point and add to the series shape:
635 // uno::Reference< drawing::XShapes > xPointGroupShape_Shapes( createGroupShape(xSeriesGroupShape_Shapes) );
636 // uno::Reference<drawing::XShape> xPointGroupShape_Shape =
637 // uno::Reference<drawing::XShape>( xPointGroupShape_Shapes, uno::UNO_QUERY );
638 //as long as we do not iterate we do not need to create an additional group for each point
639 uno::Reference
< drawing::XShapes
> xPointGroupShape_Shapes
= xSeriesGroupShape_Shapes
;
640 uno::Reference
< beans::XPropertySet
> xDataPointProperties( (*aSeriesIter
)->getPropertiesOfPoint( nPointIndex
) );
641 sal_Int32 nGeometry3D
= DataPointGeometry3D::CUBOID
;
642 if(m_nDimension
==3) try
644 xDataPointProperties
->getPropertyValue( "Geometry3D") >>= nGeometry3D
;
646 catch( const uno::Exception
& e
)
648 ASSERT_EXCEPTION( e
);
651 //@todo iterate through all subsystems to create partial points
653 //@todo select a suitable PositionHelper for this subsystem
654 BarPositionHelper
* pSubPosHelper
= pPosHelper
;
656 double fUnclippedUpperYValue
= fUpperYValue
;
658 //apply clipping to Y
659 if( !pPosHelper
->clipYRange(fLowerYValue
,fUpperYValue
) )
661 if( bDrawConnectionLines
)
662 bOnlyConnectionLinesForThisPoint
= true;
666 //@todo clipping of X and Z is not fully integrated so far, as there is a need to create different objects
668 //apply scaling to Y before calculating width (necessary to maintain gradient in clipped objects)
669 pSubPosHelper
->doLogicScaling(NULL
,&fLowerYValue
,NULL
);
670 pSubPosHelper
->doLogicScaling(NULL
,&fUpperYValue
,NULL
);
671 //scaling of X and Z is not provided as the created objects should be symmetric in that dimensions
673 pSubPosHelper
->doLogicScaling(NULL
,&fUnclippedUpperYValue
,NULL
);
675 //calculate resulting width
676 double fCompleteHeight
= bPositive
? fLogicPositiveYSum
: fLogicNegativeYSum
;
677 if( pPosHelper
->isPercentY() )
678 fCompleteHeight
= 1.0;
679 double fLogicBarWidth
= fLogicBaseWidth
;
680 double fTopHeight
=approxSub(fCompleteHeight
,fUpperYValue
);
682 fTopHeight
=approxSub(fCompleteHeight
,fLowerYValue
);
683 double fLogicYStart
= bPositive
? fLowerYValue
: fUpperYValue
;
684 double fMiddleHeight
= fUpperYValue
-fLowerYValue
;
687 double fLogicBarDepth
= 0.5;
690 if( lcl_hasGeometry3DVariableWidth(nGeometry3D
) && fCompleteHeight
!=0.0 )
692 double fHeight
= fCompleteHeight
-fLowerYValue
;
694 fHeight
= fCompleteHeight
-fUpperYValue
;
695 fLogicBarWidth
= fLogicBaseWidth
*fHeight
/(fCompleteHeight
);
696 if(fLogicBarWidth
<=0.0)
697 fLogicBarWidth
=fLogicBaseWidth
;
698 fLogicBarDepth
= fLogicBarDepth
*fHeight
/(fCompleteHeight
);
699 if(fLogicBarDepth
<=0.0)
700 fLogicBarDepth
*=-1.0;
704 //better performance for big data
705 FormerBarPoint
aFormerPoint( aSeriesFormerPointMap
[pSeries
] );
706 pPosHelper
->setCoordinateSystemResolution( m_aCoordinateSystemResolution
);
707 if( !pSeries
->isAttributedDataPoint(nPointIndex
)
709 pPosHelper
->isSameForGivenResolution( aFormerPoint
.m_fX
, aFormerPoint
.m_fUpperY
, aFormerPoint
.m_fZ
710 , fLogicX
, fUpperYValue
, fLogicZ
)
712 pPosHelper
->isSameForGivenResolution( aFormerPoint
.m_fX
, aFormerPoint
.m_fLowerY
, aFormerPoint
.m_fZ
713 , fLogicX
, fLowerYValue
, fLogicZ
)
717 m_bPointsWereSkipped
= true;
720 aSeriesFormerPointMap
[pSeries
] = FormerBarPoint(fLogicX
,fUpperYValue
,fLowerYValue
,fLogicZ
);
722 if( bDrawConnectionLines
)
724 //store point information for connection lines
726 drawing::Position3D
aLeftUpperPoint( fLogicX
-fLogicBarWidth
/2.0,fUnclippedUpperYValue
,fLogicZ
);
727 drawing::Position3D
aRightUpperPoint( fLogicX
+fLogicBarWidth
/2.0,fUnclippedUpperYValue
,fLogicZ
);
729 if( isValidPosition(aLeftUpperPoint
) )
730 AddPointToPoly( (*aSeriesIter
)->m_aPolyPolygonShape3D
, aLeftUpperPoint
);
731 if( isValidPosition(aRightUpperPoint
) )
732 AddPointToPoly( (*aSeriesIter
)->m_aPolyPolygonShape3D
, aRightUpperPoint
);
735 if( bOnlyConnectionLinesForThisPoint
)
738 //maybe additional possibility for performance improvement
739 //bool bCreateLineInsteadOfComplexGeometryDueToMissingSpace = false;
740 //pPosHelper->isSameForGivenResolution( fLogicX-fLogicBarWidth/2.0, fLowerYValue, fLogicZ
741 // , fLogicX+fLogicBarWidth/2.0, fLowerYValue, fLogicZ );
744 //create partial point
745 if( !approxEqual(fLowerYValue
,fUpperYValue
) )
747 uno::Reference
< drawing::XShape
> xShape
;
748 if( m_nDimension
==3 )
750 drawing::Position3D
aLogicBottom (fLogicX
,fLogicYStart
,fLogicZ
);
751 drawing::Position3D
aLogicLeftBottomFront (fLogicX
+fLogicBarWidth
/2.0,fLogicYStart
,fLogicZ
-fLogicBarDepth
/2.0);
752 drawing::Position3D
aLogicRightDeepTop (fLogicX
-fLogicBarWidth
/2.0,fLogicYStart
+fMiddleHeight
,fLogicZ
+fLogicBarDepth
/2.0);
753 drawing::Position3D
aLogicTopTop (fLogicX
,fLogicYStart
+fMiddleHeight
+fTopHeight
,fLogicZ
);
755 uno::Reference
< XTransformation
> xTransformation
= pSubPosHelper
->getTransformationScaledLogicToScene();
757 //transformation 3) -> 4)
758 drawing::Position3D
aTransformedBottom ( SequenceToPosition3D( xTransformation
->transform( Position3DToSequence(aLogicBottom
) ) ) );
759 drawing::Position3D
aTransformedLeftBottomFront ( SequenceToPosition3D( xTransformation
->transform( Position3DToSequence(aLogicLeftBottomFront
) ) ) );
760 drawing::Position3D
aTransformedRightDeepTop ( SequenceToPosition3D( xTransformation
->transform( Position3DToSequence(aLogicRightDeepTop
) ) ) );
761 drawing::Position3D
aTransformedTopTop ( SequenceToPosition3D( xTransformation
->transform( Position3DToSequence(aLogicTopTop
) ) ) );
763 drawing::Direction3D aSize
= aTransformedRightDeepTop
- aTransformedLeftBottomFront
;
764 drawing::Direction3D
aTopSize( aTransformedTopTop
- aTransformedRightDeepTop
);
765 fTopHeight
= aTopSize
.DirectionY
;
767 sal_Int32 nRotateZAngleHundredthDegree
= 0;
768 if( pPosHelper
->isSwapXAndY() )
770 fTopHeight
= aTopSize
.DirectionX
;
771 nRotateZAngleHundredthDegree
= 90*100;
772 aSize
= drawing::Direction3D(aSize
.DirectionY
,aSize
.DirectionX
,aSize
.DirectionZ
);
775 if( aSize
.DirectionX
< 0 )
776 aSize
.DirectionX
*= -1.0;
777 if( aSize
.DirectionZ
< 0 )
778 aSize
.DirectionZ
*= -1.0;
782 xShape
= createDataPoint3D_Bar(
783 xPointGroupShape_Shapes
, aTransformedBottom
, aSize
, fTopHeight
, nRotateZAngleHundredthDegree
784 , xDataPointProperties
, nGeometry3D
);
786 else //m_nDimension!=3
788 // performance improvement: alloc the sequence before the rendering
789 // otherwise we have 2 realloc calls
790 drawing::PolyPolygonShape3D aPoly
;
791 aPoly
.SequenceX
.realloc(1);
792 aPoly
.SequenceY
.realloc(1);
793 aPoly
.SequenceZ
.realloc(1);
794 drawing::Position3D
aLeftUpperPoint( fLogicX
-fLogicBarWidth
/2.0,fUpperYValue
,fLogicZ
);
795 drawing::Position3D
aRightUpperPoint( fLogicX
+fLogicBarWidth
/2.0,fUpperYValue
,fLogicZ
);
797 AddPointToPoly( aPoly
, drawing::Position3D( fLogicX
-fLogicBarWidth
/2.0,fLowerYValue
,fLogicZ
) );
798 AddPointToPoly( aPoly
, drawing::Position3D( fLogicX
+fLogicBarWidth
/2.0,fLowerYValue
,fLogicZ
) );
799 AddPointToPoly( aPoly
, aRightUpperPoint
);
800 AddPointToPoly( aPoly
, aLeftUpperPoint
);
801 AddPointToPoly( aPoly
, drawing::Position3D( fLogicX
-fLogicBarWidth
/2.0,fLowerYValue
,fLogicZ
) );
802 pPosHelper
->transformScaledLogicToScene( aPoly
);
803 xShape
= m_pShapeFactory
->createArea2D( xPointGroupShape_Shapes
, aPoly
);
804 setMappedProperties( xShape
, xDataPointProperties
, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
807 if(bHasFillColorMapping
)
809 double nPropVal
= pSeries
->getValueByProperty(nPointIndex
, "FillColor");
810 if(!rtl::math::isNan(nPropVal
))
812 uno::Reference
< beans::XPropertySet
> xProps( xShape
, uno::UNO_QUERY_THROW
);
813 xProps
->setPropertyValue("FillColor", uno::makeAny(static_cast<sal_Int32
>(nPropVal
)));
816 //set name/classified ObjectID (CID)
817 ShapeFactory::setShapeName(xShape
818 , ObjectIdentifier::createPointCID(
819 (*aSeriesIter
)->getPointCID_Stub(),nPointIndex
) );
823 createErrorBar_Y( aUnscaledLogicPosition
, **aSeriesIter
, nPointIndex
, m_xLogicTarget
, &fLogicX
);
825 //create data point label
826 if( (**aSeriesIter
).getDataPointLabelIfLabel(nPointIndex
) )
828 double fLogicSum
= aLogicYSumMap
[nAttachedAxisIndex
];
830 LabelAlignment
eAlignment(LABEL_ALIGN_CENTER
);
831 sal_Int32 nLabelPlacement
= pSeries
->getLabelPlacement( nPointIndex
, m_xChartTypeModel
, m_nDimension
, pPosHelper
->isSwapXAndY() );
833 double fLowerBarDepth
= fLogicBarDepth
;
834 double fUpperBarDepth
= fLogicBarDepth
;
836 double fOuterBarDepth
= fLogicBarDepth
;
837 if( lcl_hasGeometry3DVariableWidth(nGeometry3D
) && fCompleteHeight
!=0.0 )
839 fOuterBarDepth
= fLogicBarDepth
* (fTopHeight
)/(fabs(fCompleteHeight
));
840 fLowerBarDepth
= (fBaseValue
< fUpperYValue
) ? fabs(fLogicBarDepth
) : fabs(fOuterBarDepth
);
841 fUpperBarDepth
= (fBaseValue
< fUpperYValue
) ? fabs(fOuterBarDepth
) : fabs(fLogicBarDepth
);
845 awt::Point aScreenPosition2D
= getLabelScreenPositionAndAlignment(
846 eAlignment
, nLabelPlacement
, fLogicX
, fLowerYValue
, fUpperYValue
, fLogicZ
,
847 fLowerBarDepth
, fUpperBarDepth
, fBaseValue
, pPosHelper
);
848 sal_Int32 nOffset
= 0;
849 if(LABEL_ALIGN_CENTER
!=eAlignment
)
851 nOffset
= 100;//add some spacing //@todo maybe get more intelligent values
852 if( m_nDimension
== 3 )
856 xTextTarget
, **aSeriesIter
, nPointIndex
,
857 fLogicValueForLabeDisplay
, fLogicSum
, aScreenPosition2D
, eAlignment
, nOffset
);
860 }//end iteration through partial points
862 }//next series in x slot (next y slot)
866 if( bDrawConnectionLines
)
868 ::std::vector
< ::std::vector
< VDataSeriesGroup
> >::iterator aZSlotIter
= m_aZSlots
.begin();
869 const ::std::vector
< ::std::vector
< VDataSeriesGroup
> >::const_iterator aZSlotEnd
= m_aZSlots
.end();
870 for( sal_Int32 nZ
=1; aZSlotIter
!= aZSlotEnd
; ++aZSlotIter
, nZ
++ )
872 ::std::vector
< VDataSeriesGroup
>::iterator aXSlotIter
= aZSlotIter
->begin();
873 const ::std::vector
< VDataSeriesGroup
>::const_iterator aXSlotEnd
= aZSlotIter
->end();
875 BarPositionHelper
* pPosHelper
= m_pMainPosHelper
;
876 if( aXSlotIter
!= aXSlotEnd
)
878 sal_Int32 nAttachedAxisIndex
= aXSlotIter
->getAttachedAxisIndexForFirstSeries();
879 //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot
880 pPosHelper
= dynamic_cast<BarPositionHelper
*>(&( this->getPlottingPositionHelper( nAttachedAxisIndex
) ) );
882 pPosHelper
= m_pMainPosHelper
;
884 PlotterBase::m_pPosHelper
= pPosHelper
;
886 //iterate through all x slots in this category
887 for( double fSlotX
=0; aXSlotIter
!= aXSlotEnd
; ++aXSlotIter
, fSlotX
+=1.0 )
889 ::std::vector
< VDataSeries
* >* pSeriesList
= &(aXSlotIter
->m_aSeriesVector
);
891 ::std::vector
< VDataSeries
* >::const_iterator aSeriesIter
= pSeriesList
->begin();
892 const ::std::vector
< VDataSeries
* >::const_iterator aSeriesEnd
= pSeriesList
->end();
893 //iterate through all series in this x slot
894 for( ; aSeriesIter
!= aSeriesEnd
; ++aSeriesIter
)
896 VDataSeries
* pSeries( *aSeriesIter
);
899 drawing::PolyPolygonShape3D
* pSeriesPoly
= &pSeries
->m_aPolyPolygonShape3D
;
900 if(!ShapeFactory::hasPolygonAnyLines(*pSeriesPoly
))
903 drawing::PolyPolygonShape3D aPoly
;
904 Clipping::clipPolygonAtRectangle( *pSeriesPoly
, pPosHelper
->getScaledLogicClipDoubleRect(), aPoly
);
906 if(!ShapeFactory::hasPolygonAnyLines(aPoly
))
909 //transformation 3) -> 4)
910 pPosHelper
->transformScaledLogicToScene( aPoly
);
912 uno::Reference
< drawing::XShapes
> xSeriesGroupShape_Shapes(
913 getSeriesGroupShape(*aSeriesIter
, xSeriesTarget
) );
914 uno::Reference
< drawing::XShape
> xShape( m_pShapeFactory
->createLine2D(
915 xSeriesGroupShape_Shapes
, PolyToPointSequence( aPoly
) ) );
916 setMappedProperties( xShape
, pSeries
->getPropertiesOfSeries()
917 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
923 /* @todo remove series shapes if empty
928 "skipped points: " << nSkippedPoints
<< " created points: "
934 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */