1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24
25 // MARKER(update_precomp.py): autogen include statement, do not remove
26 #include "precompiled_chart2.hxx"
27
28 #include "BarChart.hxx"
29 #include "ShapeFactory.hxx"
30 //#include "chartview/servicenames_charttypes.hxx"
31 //#include "servicenames_coosystems.hxx"
32 #include "CommonConverters.hxx"
33 #include "ObjectIdentifier.hxx"
34 #include "LabelPositionHelper.hxx"
35 #include "BarPositionHelper.hxx"
36 #include "macros.hxx"
37 #include "AxisIndexDefines.hxx"
38 #include "Clipping.hxx"
39 #include "DateHelper.hxx"
40
41 #include <com/sun/star/chart/DataLabelPlacement.hpp>
42
43 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
44 #include <tools/debug.hxx>
45 #include <rtl/math.hxx>
46
47 //.............................................................................
48 namespace chart
49 {
50 //.............................................................................
51 using namespace ::com::sun::star;
52 using namespace ::rtl::math;
53 using namespace ::com::sun::star::chart2;
54
55 //-----------------------------------------------------------------------------
56 //-----------------------------------------------------------------------------
57 //-----------------------------------------------------------------------------
58
BarChart(const uno::Reference<XChartType> & xChartTypeModel,sal_Int32 nDimensionCount)59 BarChart::BarChart( const uno::Reference<XChartType>& xChartTypeModel
60 , sal_Int32 nDimensionCount )
61 : VSeriesPlotter( xChartTypeModel, nDimensionCount )
62 , m_pMainPosHelper( new BarPositionHelper() )
63 {
64 PlotterBase::m_pPosHelper = m_pMainPosHelper;
65 VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper;
66
67 try
68 {
69 if( m_xChartTypeModelProps.is() )
70 {
71 m_xChartTypeModelProps->getPropertyValue( C2U( "OverlapSequence" ) ) >>= m_aOverlapSequence;
72 m_xChartTypeModelProps->getPropertyValue( C2U( "GapwidthSequence" ) ) >>= m_aGapwidthSequence;
73 }
74 }
75 catch( uno::Exception& e )
76 {
77 ASSERT_EXCEPTION( e );
78 }
79 }
80
~BarChart()81 BarChart::~BarChart()
82 {
83 delete m_pMainPosHelper;
84 }
85
86 //-------------------------------------------------------------------------
87
getPlottingPositionHelper(sal_Int32 nAxisIndex) const88 PlottingPositionHelper& BarChart::getPlottingPositionHelper( sal_Int32 nAxisIndex ) const
89 {
90 PlottingPositionHelper& rPosHelper = VSeriesPlotter::getPlottingPositionHelper( nAxisIndex );
91 BarPositionHelper* pBarPosHelper = dynamic_cast<BarPositionHelper*>(&rPosHelper);
92 if( pBarPosHelper && nAxisIndex >= 0 )
93 {
94 if( nAxisIndex < m_aOverlapSequence.getLength() )
95 pBarPosHelper->setInnerDistance( -m_aOverlapSequence[nAxisIndex]/100.0 );
96 if( nAxisIndex < m_aGapwidthSequence.getLength() )
97 pBarPosHelper->setOuterDistance( m_aGapwidthSequence[nAxisIndex]/100.0 );
98 }
99 return rPosHelper;
100 }
101
getPreferredDiagramAspectRatio() const102 drawing::Direction3D BarChart::getPreferredDiagramAspectRatio() const
103 {
104 drawing::Direction3D aRet(1.0,1.0,1.0);
105 if( m_nDimension == 3 )
106 {
107 aRet = drawing::Direction3D(1.0,-1.0,1.0);
108 BarPositionHelper* pPosHelper = dynamic_cast<BarPositionHelper*>(&( this->getPlottingPositionHelper( MAIN_AXIS_INDEX) ) );
109 drawing::Direction3D aScale( pPosHelper->getScaledLogicWidth() );
110 if(aScale.DirectionX!=0.0)
111 {
112 double fXSlotCount = 1.0;
113 if(!m_aZSlots.empty())
114 fXSlotCount = m_aZSlots.begin()->size();
115
116 aRet.DirectionZ = aScale.DirectionZ/(aScale.DirectionX + aScale.DirectionX*(fXSlotCount-1.0)*pPosHelper->getScaledSlotWidth());
117 }
118 else
119 return VSeriesPlotter::getPreferredDiagramAspectRatio();
120 if(aRet.DirectionZ<0.05)
121 aRet.DirectionZ=0.05;
122 if(aRet.DirectionZ>10)
123 aRet.DirectionZ=10;
124
125 if( m_pMainPosHelper && m_pMainPosHelper->isSwapXAndY() )
126 {
127 double fTemp = aRet.DirectionX;
128 aRet.DirectionX = aRet.DirectionY;
129 aRet.DirectionY = fTemp;
130 }
131 }
132 else
133 aRet = drawing::Direction3D(-1,-1,-1);
134 return aRet;
135 }
136
keepAspectRatio() const137 bool BarChart::keepAspectRatio() const
138 {
139 if( m_nDimension == 3 )
140 return true;
141 return true;
142 }
143
getLabelScreenPositionAndAlignment(LabelAlignment & rAlignment,sal_Int32 nLabelPlacement,double fScaledX,double fScaledLowerYValue,double fScaledUpperYValue,double fScaledZ,double fScaledLowerBarDepth,double fScaledUpperBarDepth,double fBaseValue,BarPositionHelper * pPosHelper) const144 awt::Point BarChart::getLabelScreenPositionAndAlignment(
145 LabelAlignment& rAlignment, sal_Int32 nLabelPlacement
146 , double fScaledX, double fScaledLowerYValue, double fScaledUpperYValue, double fScaledZ
147 , double fScaledLowerBarDepth, double fScaledUpperBarDepth, double fBaseValue
148 , BarPositionHelper* pPosHelper
149 ) const
150 {
151 double fX = fScaledX;
152 double fY = fScaledUpperYValue;
153 double fZ = fScaledZ;
154 bool bReverse = !pPosHelper->isMathematicalOrientationY();
155 bool bNormalOutside = (!bReverse == !!(fBaseValue < fScaledUpperYValue));
156 double fDepth = fScaledUpperBarDepth;
157
158 switch(nLabelPlacement)
159 {
160 case ::com::sun::star::chart::DataLabelPlacement::TOP:
161 {
162 if( !pPosHelper->isSwapXAndY() )
163 {
164 fY = bReverse ? fScaledLowerYValue : fScaledUpperYValue;
165 rAlignment = LABEL_ALIGN_TOP;
166 if(3==m_nDimension)
167 fDepth = bReverse ? fabs(fScaledLowerBarDepth) : fabs(fScaledUpperBarDepth);
168 }
169 else
170 {
171 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
172 rAlignment = LABEL_ALIGN_CENTER;
173 DBG_ERROR( "top label placement is not really supported by horizontal bar charts" );
174 }
175 }
176 break;
177 case ::com::sun::star::chart::DataLabelPlacement::BOTTOM:
178 {
179 if(!pPosHelper->isSwapXAndY())
180 {
181 fY = bReverse ? fScaledUpperYValue : fScaledLowerYValue;
182 rAlignment = LABEL_ALIGN_BOTTOM;
183 if(3==m_nDimension)
184 fDepth = bReverse ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth);
185 }
186 else
187 {
188 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
189 rAlignment = LABEL_ALIGN_CENTER;
190 DBG_ERROR( "bottom label placement is not supported by horizontal bar charts" );
191 }
192 }
193 break;
194 case ::com::sun::star::chart::DataLabelPlacement::LEFT:
195 {
196 if( pPosHelper->isSwapXAndY() )
197 {
198 fY = bReverse ? fScaledUpperYValue : fScaledLowerYValue;
199 rAlignment = LABEL_ALIGN_LEFT;
200 if(3==m_nDimension)
201 fDepth = bReverse ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth);
202 }
203 else
204 {
205 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
206 rAlignment = LABEL_ALIGN_CENTER;
207 DBG_ERROR( "left label placement is not supported by column charts" );
208 }
209 }
210 break;
211 case ::com::sun::star::chart::DataLabelPlacement::RIGHT:
212 {
213 if( pPosHelper->isSwapXAndY() )
214 {
215 fY = bReverse ? fScaledLowerYValue : fScaledUpperYValue;
216 rAlignment = LABEL_ALIGN_RIGHT;
217 if(3==m_nDimension)
218 fDepth = bReverse ? fabs(fScaledLowerBarDepth) : fabs(fScaledUpperBarDepth);
219 }
220 else
221 {
222 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
223 rAlignment = LABEL_ALIGN_CENTER;
224 DBG_ERROR( "right label placement is not supported by column charts" );
225 }
226 }
227 break;
228 case ::com::sun::star::chart::DataLabelPlacement::OUTSIDE:
229 {
230 fY = (fBaseValue < fScaledUpperYValue) ? fScaledUpperYValue : fScaledLowerYValue;
231 if( pPosHelper->isSwapXAndY() )
232 rAlignment = bNormalOutside ? LABEL_ALIGN_RIGHT : LABEL_ALIGN_LEFT;
233 else
234 rAlignment = bNormalOutside ? LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM;
235 if(3==m_nDimension)
236 fDepth = (fBaseValue < fScaledUpperYValue) ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth);
237 }
238 break;
239 case ::com::sun::star::chart::DataLabelPlacement::INSIDE:
240 {
241 fY = (fBaseValue < fScaledUpperYValue) ? fScaledUpperYValue : fScaledLowerYValue;
242 if( pPosHelper->isSwapXAndY() )
243 rAlignment = bNormalOutside ? LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT;
244 else
245 rAlignment = bNormalOutside ? LABEL_ALIGN_BOTTOM : LABEL_ALIGN_TOP;
246 if(3==m_nDimension)
247 fDepth = (fBaseValue < fScaledUpperYValue) ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth);
248 }
249 break;
250 case ::com::sun::star::chart::DataLabelPlacement::NEAR_ORIGIN:
251 {
252 fY = (fBaseValue < fScaledUpperYValue) ? fScaledLowerYValue : fScaledUpperYValue;
253 if( pPosHelper->isSwapXAndY() )
254 rAlignment = bNormalOutside ? LABEL_ALIGN_RIGHT : LABEL_ALIGN_LEFT;
255 else
256 rAlignment = bNormalOutside ? LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM;
257 if(3==m_nDimension)
258 fDepth = (fBaseValue < fScaledUpperYValue) ? fabs(fScaledLowerBarDepth) : fabs(fScaledUpperBarDepth);
259 }
260 break;
261 case ::com::sun::star::chart::DataLabelPlacement::CENTER:
262 fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0;
263 rAlignment = LABEL_ALIGN_CENTER;
264 if(3==m_nDimension)
265 fDepth = fabs(fScaledUpperBarDepth-fScaledLowerBarDepth)/2.0;
266 break;
267 default:
268 DBG_ERROR("this label alignment is not implemented yet");
269
270 break;
271 }
272 if(3==m_nDimension)
273 fZ -= fDepth/2.0;
274
275 drawing::Position3D aScenePosition3D( pPosHelper->
276 transformScaledLogicToScene( fX, fY, fZ, true ) );
277 return LabelPositionHelper(pPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory)
278 .transformSceneToScreenPosition( aScenePosition3D );
279 }
280
createDataPoint3D_Bar(const uno::Reference<drawing::XShapes> & xTarget,const drawing::Position3D & rPosition,const drawing::Direction3D & rSize,double fTopHeight,sal_Int32 nRotateZAngleHundredthDegree,const uno::Reference<beans::XPropertySet> & xObjectProperties,sal_Int32 nGeometry3D)281 uno::Reference< drawing::XShape > BarChart::createDataPoint3D_Bar(
282 const uno::Reference< drawing::XShapes >& xTarget
283 , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
284 , double fTopHeight, sal_Int32 nRotateZAngleHundredthDegree
285 , const uno::Reference< beans::XPropertySet >& xObjectProperties
286 , sal_Int32 nGeometry3D )
287 {
288 bool bRoundedEdges = true;
289 try
290 {
291 if( xObjectProperties.is() )
292 {
293 sal_Int16 nPercentDiagonal = 0;
294 xObjectProperties->getPropertyValue( C2U( "PercentDiagonal" ) ) >>= nPercentDiagonal;
295 if( nPercentDiagonal < 5 )
296 bRoundedEdges = false;
297 }
298 }
299 catch( uno::Exception& e )
300 {
301 ASSERT_EXCEPTION( e );
302 }
303
304 uno::Reference< drawing::XShape > xShape(NULL);
305 switch( nGeometry3D )
306 {
307 case DataPointGeometry3D::CYLINDER:
308 xShape = m_pShapeFactory->createCylinder( xTarget, rPosition, rSize, nRotateZAngleHundredthDegree );
309 break;
310 case DataPointGeometry3D::CONE:
311 xShape = m_pShapeFactory->createCone( xTarget, rPosition, rSize, fTopHeight, nRotateZAngleHundredthDegree );
312 break;
313 case DataPointGeometry3D::PYRAMID:
314 xShape = m_pShapeFactory->createPyramid( xTarget, rPosition, rSize, fTopHeight, nRotateZAngleHundredthDegree>0
315 , xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
316 break;
317 case DataPointGeometry3D::CUBOID:
318 default:
319 xShape = m_pShapeFactory->createCube( xTarget, rPosition, rSize
320 , nRotateZAngleHundredthDegree, xObjectProperties
321 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), bRoundedEdges );
322 return xShape;
323 }
324 if( nGeometry3D != DataPointGeometry3D::PYRAMID )
325 this->setMappedProperties( xShape, xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
326 return xShape;
327 }
328
329 namespace
330 {
lcl_hasGeometry3DVariableWidth(sal_Int32 nGeometry3D)331 bool lcl_hasGeometry3DVariableWidth( sal_Int32 nGeometry3D )
332 {
333 bool bRet = false;
334 switch( nGeometry3D )
335 {
336 case DataPointGeometry3D::PYRAMID:
337 case DataPointGeometry3D::CONE:
338 bRet = true;
339 break;
340 case DataPointGeometry3D::CUBOID:
341 case DataPointGeometry3D::CYLINDER:
342 default:
343 bRet = false;
344 break;
345 }
346 return bRet;
347 }
348 }// end anonymous namespace
349
addSeries(VDataSeries * pSeries,sal_Int32 zSlot,sal_Int32 xSlot,sal_Int32 ySlot)350 void BarChart::addSeries( VDataSeries* pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot )
351 {
352 if( !pSeries )
353 return;
354 if(m_nDimension==2)
355 {
356 //2ND_AXIS_IN_BARS put series on second scales to different z slot as temporary workaround
357 //this needs to be redesigned if 3d bars are also able to display secondary axes
358
359 sal_Int32 nAxisIndex = pSeries->getAttachedAxisIndex();
360 zSlot = nAxisIndex;
361
362 if( !pSeries->getGroupBarsPerAxis() )
363 zSlot = 0;
364 if(zSlot>=static_cast<sal_Int32>(m_aZSlots.size()))
365 m_aZSlots.resize(zSlot+1);
366 }
367 VSeriesPlotter::addSeries( pSeries, zSlot, xSlot, ySlot );
368 }
369
370 //better performance for big data
371 struct FormerBarPoint
372 {
FormerBarPointchart::FormerBarPoint373 FormerBarPoint( double fX, double fUpperY, double fLowerY, double fZ )
374 : m_fX(fX), m_fUpperY(fUpperY), m_fLowerY(fLowerY), m_fZ(fZ)
375 {}
FormerBarPointchart::FormerBarPoint376 FormerBarPoint()
377 {
378 ::rtl::math::setNan( &m_fX );
379 ::rtl::math::setNan( &m_fUpperY );
380 ::rtl::math::setNan( &m_fLowerY );
381 ::rtl::math::setNan( &m_fZ );
382 }
383
384 double m_fX;
385 double m_fUpperY;
386 double m_fLowerY;
387 double m_fZ;
388 };
389
adaptOverlapAndGapwidthForGroupBarsPerAxis()390 void BarChart::adaptOverlapAndGapwidthForGroupBarsPerAxis()
391 {
392 //adapt m_aOverlapSequence and m_aGapwidthSequence for the groupBarsPerAxis feature
393 //thus the different series use the same settings
394
395 VDataSeries* pFirstSeries = getFirstSeries();
396 if(pFirstSeries && !pFirstSeries->getGroupBarsPerAxis() )
397 {
398 sal_Int32 nAxisIndex = pFirstSeries->getAttachedAxisIndex();
399 sal_Int32 nN = 0;
400 sal_Int32 nUseThisIndex = nAxisIndex;
401 if( nUseThisIndex < 0 || nUseThisIndex >= m_aOverlapSequence.getLength() )
402 nUseThisIndex = 0;
403 for( nN = 0; nN < m_aOverlapSequence.getLength(); nN++ )
404 {
405 if(nN!=nUseThisIndex)
406 m_aOverlapSequence[nN] = m_aOverlapSequence[nUseThisIndex];
407 }
408
409 nUseThisIndex = nAxisIndex;
410 if( nUseThisIndex < 0 || nUseThisIndex >= m_aGapwidthSequence.getLength() )
411 nUseThisIndex = 0;
412 for( nN = 0; nN < m_aGapwidthSequence.getLength(); nN++ )
413 {
414 if(nN!=nUseThisIndex)
415 m_aGapwidthSequence[nN] = m_aGapwidthSequence[nUseThisIndex];
416 }
417 }
418 }
419
createShapes()420 void BarChart::createShapes()
421 {
422 if( m_aZSlots.begin() == m_aZSlots.end() ) //no series
423 return;
424
425 DBG_ASSERT(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"BarChart is not proper initialized");
426 if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is()))
427 return;
428
429 //the text labels should be always on top of the other series shapes
430 //therefore create an own group for the texts to move them to front
431 //(because the text group is created after the series group the texts are displayed on top)
432
433 //the regression curves should always be on top of the bars but beneath the text labels
434 //to achieve this the regression curve target is created after the series target and before the text target
435
436 uno::Reference< drawing::XShapes > xSeriesTarget(
437 createGroupShape( m_xLogicTarget,rtl::OUString() ));
438 uno::Reference< drawing::XShapes > xRegressionCurveTarget(
439 createGroupShape( m_xLogicTarget,rtl::OUString() ));
440 uno::Reference< drawing::XShapes > xTextTarget(
441 m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() ));
442
443
444 //---------------------------------------------
445 uno::Reference< drawing::XShapes > xRegressionCurveEquationTarget(
446 m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() ));
447 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
448
449 double fLogicZ = 1.0;//as defined
450
451 bool bDrawConnectionLines = false;
452 bool bDrawConnectionLinesInited = false;
453 bool bOnlyConnectionLinesForThisPoint = false;
454
455 adaptOverlapAndGapwidthForGroupBarsPerAxis();
456
457 //better performance for big data
458 std::map< VDataSeries*, FormerBarPoint > aSeriesFormerPointMap;
459 m_bPointsWereSkipped = false;
460 sal_Int32 nSkippedPoints = 0;
461 sal_Int32 nCreatedPoints = 0;
462 //
463
464 sal_Int32 nStartIndex = 0;
465 sal_Int32 nEndIndex = VSeriesPlotter::getPointCount();
466 //=============================================================================
467 //iterate through all x values per indices
468 for( sal_Int32 nPointIndex = nStartIndex; nPointIndex < nEndIndex; nPointIndex++ )
469 {
470 ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin();
471 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
472
473 //sum up the values for all series in a complete z zlot per attached axis
474 ::std::map< sal_Int32, double > aLogicYSumMap;
475 for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ )
476 {
477 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
478 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
479
480 for( aXSlotIter = aZSlotIter->begin(); aXSlotIter != aXSlotEnd; aXSlotIter++ )
481 {
482 sal_Int32 nAttachedAxisIndex = aXSlotIter->getAttachedAxisIndexForFirstSeries();
483 if( aLogicYSumMap.find(nAttachedAxisIndex)==aLogicYSumMap.end() )
484 aLogicYSumMap[nAttachedAxisIndex]=0.0;
485
486 double fMinimumY = 0.0, fMaximumY = 0.0;
487 aXSlotIter->calculateYMinAndMaxForCategory( nPointIndex
488 , isSeperateStackingForDifferentSigns( 1 ), fMinimumY, fMaximumY, nAttachedAxisIndex );
489
490 if( !::rtl::math::isNan( fMaximumY ) && fMaximumY > 0)
491 aLogicYSumMap[nAttachedAxisIndex] += fMaximumY;
492 if( !::rtl::math::isNan( fMinimumY ) && fMinimumY < 0)
493 aLogicYSumMap[nAttachedAxisIndex] += fabs(fMinimumY);
494 }
495 }
496
497 //=============================================================================
498 aZSlotIter = m_aZSlots.begin();
499 for( sal_Int32 nZ=1; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ )
500 {
501 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
502 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
503
504 //=============================================================================
505 //iterate through all x slots in this category
506 double fSlotX=0;
507 for( aXSlotIter = aZSlotIter->begin(); aXSlotIter != aXSlotEnd; aXSlotIter++, fSlotX+=1.0 )
508 {
509 sal_Int32 nAttachedAxisIndex = 0;
510 BarPositionHelper* pPosHelper = m_pMainPosHelper;
511 if( aXSlotIter != aXSlotEnd )
512 {
513 nAttachedAxisIndex = aXSlotIter->getAttachedAxisIndexForFirstSeries();
514 //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot
515 pPosHelper = dynamic_cast<BarPositionHelper*>(&( this->getPlottingPositionHelper( nAttachedAxisIndex ) ) );
516 if(!pPosHelper)
517 pPosHelper = m_pMainPosHelper;
518 }
519 PlotterBase::m_pPosHelper = pPosHelper;
520
521 //update/create information for current group
522 pPosHelper->updateSeriesCount( aZSlotIter->size() );
523 double fLogicBaseWidth = pPosHelper->getScaledSlotWidth();
524
525 ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
526
527 // get distance from base value to maximum and minimum
528
529 double fMinimumY = 0.0, fMaximumY = 0.0;
530 aXSlotIter->calculateYMinAndMaxForCategory( nPointIndex
531 , isSeperateStackingForDifferentSigns( 1 ), fMinimumY, fMaximumY, nAttachedAxisIndex );
532
533 double fLogicPositiveYSum = 0.0;
534 if( !::rtl::math::isNan( fMaximumY ) )
535 fLogicPositiveYSum = fMaximumY;
536
537 double fLogicNegativeYSum = 0.0;
538 if( !::rtl::math::isNan( fMinimumY ) )
539 fLogicNegativeYSum = fMinimumY;
540
541 if( pPosHelper->isPercentY() )
542 {
543 /* #i70395# fLogicPositiveYSum contains sum of all positive
544 values, if any, otherwise the highest negative value.
545 fLogicNegativeYSum contains sum of all negative values,
546 if any, otherwise the lowest positive value.
547 Afterwards, fLogicPositiveYSum will contain the maximum
548 (positive) value that is related to 100%. */
549
550 // do nothing if there are positive values only
551 if( fLogicNegativeYSum < 0.0 )
552 {
553 // fLogicPositiveYSum<0 => negative values only, use absolute of negative sum
554 if( fLogicPositiveYSum < 0.0 )
555 fLogicPositiveYSum = -fLogicNegativeYSum;
556 // otherwise there are positive and negative values, calculate total distance
557 else
558 fLogicPositiveYSum -= fLogicNegativeYSum;
559 }
560 fLogicNegativeYSum = 0.0;
561 }
562
563 double fBaseValue = 0.0;
564 if( !pPosHelper->isPercentY() && pSeriesList->size()<=1 )
565 fBaseValue = pPosHelper->getBaseValueY();
566 double fPositiveLogicYForNextSeries = fBaseValue;
567 double fNegativeLogicYForNextSeries = fBaseValue;
568
569 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin();
570 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end();
571 aSeriesIter = pSeriesList->begin();
572 //=============================================================================
573 //iterate through all series in this x slot
574 for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ )
575 {
576 VDataSeries* pSeries( *aSeriesIter );
577 if(!pSeries)
578 continue;
579
580 bOnlyConnectionLinesForThisPoint = false;
581
582 if(nPointIndex==nStartIndex)//do not create a regression line for each point
583 createRegressionCurvesShapes( **aSeriesIter, xRegressionCurveTarget, xRegressionCurveEquationTarget,
584 m_pPosHelper->maySkipPointsInRegressionCalculation());
585
586 if( !bDrawConnectionLinesInited )
587 {
588 bDrawConnectionLines = pSeries->getConnectBars();
589 if( m_nDimension==3 )
590 bDrawConnectionLines = false;
591 if( bDrawConnectionLines && pSeriesList->size()==1 )
592 {
593 //detect whether we have a stacked chart or not:
594 StackingDirection eDirection = pSeries->getStackingDirection();
595 if( eDirection != StackingDirection_Y_STACKING )
596 bDrawConnectionLines = false;
597 }
598 bDrawConnectionLinesInited = true;
599 }
600
601 //------------
602
603 uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes(
604 getSeriesGroupShape(*aSeriesIter, xSeriesTarget) );
605
606 //collect data point information (logic coordinates, style ):
607 double fUnscaledLogicX = (*aSeriesIter)->getXValue( nPointIndex );
608 fUnscaledLogicX = DateHelper::RasterizeDateValue( fUnscaledLogicX, m_aNullDate, m_nTimeResolution );
609 if(fUnscaledLogicX<pPosHelper->getLogicMinX())
610 continue;//point not visible
611 if(fUnscaledLogicX>pPosHelper->getLogicMaxX())
612 continue;//point not visible
613 if(pPosHelper->isStrongLowerRequested(0) && fUnscaledLogicX==pPosHelper->getLogicMaxX())
614 continue;//point not visible
615 double fLogicX = pPosHelper->getScaledSlotPos( fUnscaledLogicX, fSlotX );
616
617 double fLogicBarHeight = (*aSeriesIter)->getYValue( nPointIndex );
618 if( ::rtl::math::isNan( fLogicBarHeight )) //no value at this category
619 continue;
620
621 double fLogicValueForLabeDisplay = fLogicBarHeight;
622 fLogicBarHeight-=fBaseValue;
623
624 if( pPosHelper->isPercentY() )
625 {
626 if(fLogicPositiveYSum!=0.0)
627 fLogicBarHeight = fabs( fLogicBarHeight )/fLogicPositiveYSum;
628 else
629 fLogicBarHeight = 0.0;
630 }
631
632 //sort negative and positive values, to display them on different sides of the x axis
633 bool bPositive = fLogicBarHeight >= 0.0;
634 double fLowerYValue = bPositive ? fPositiveLogicYForNextSeries : fNegativeLogicYForNextSeries;
635 double fUpperYValue = fLowerYValue+fLogicBarHeight;
636 if( bPositive )
637 fPositiveLogicYForNextSeries += fLogicBarHeight;
638 else
639 fNegativeLogicYForNextSeries += fLogicBarHeight;
640
641 if(m_nDimension==3)
642 fLogicZ = nZ+0.5;
643
644 drawing::Position3D aUnscaledLogicPosition( fUnscaledLogicX, fUpperYValue, fLogicZ );
645
646 //@todo ... start an iteration over the different breaks of the axis
647 //each subsystem may add an additional shape to form the whole point
648 //create a group shape for this point and add to the series shape:
649 // uno::Reference< drawing::XShapes > xPointGroupShape_Shapes( createGroupShape(xSeriesGroupShape_Shapes) );
650 // uno::Reference<drawing::XShape> xPointGroupShape_Shape =
651 // uno::Reference<drawing::XShape>( xPointGroupShape_Shapes, uno::UNO_QUERY );
652 //as long as we do not iterate we do not need to create an additional group for each point
653 uno::Reference< drawing::XShapes > xPointGroupShape_Shapes = xSeriesGroupShape_Shapes;
654 uno::Reference< beans::XPropertySet > xDataPointProperties( (*aSeriesIter)->getPropertiesOfPoint( nPointIndex ) );
655 sal_Int32 nGeometry3D = DataPointGeometry3D::CUBOID;
656 if(m_nDimension==3) try
657 {
658 xDataPointProperties->getPropertyValue( C2U( "Geometry3D" )) >>= nGeometry3D;
659 }
660 catch( uno::Exception& e )
661 {
662 ASSERT_EXCEPTION( e );
663 }
664
665 //@todo iterate through all subsystems to create partial points
666 {
667 //@todo select a suiteable PositionHelper for this subsystem
668 BarPositionHelper* pSubPosHelper = pPosHelper;
669
670 double fUnclippedUpperYValue = fUpperYValue;
671
672 //apply clipping to Y
673 if( !pPosHelper->clipYRange(fLowerYValue,fUpperYValue) )
674 {
675 if( bDrawConnectionLines )
676 bOnlyConnectionLinesForThisPoint = true;
677 else
678 continue;
679 }
680 //@todo clipping of X and Z is not fully integrated so far, as there is a need to create different objects
681
682 //apply scaling to Y before calculating width (necessary to maintain gradient in clipped objects)
683 pSubPosHelper->doLogicScaling(NULL,&fLowerYValue,NULL);
684 pSubPosHelper->doLogicScaling(NULL,&fUpperYValue,NULL);
685 //scaling of X and Z is not provided as the created objects should be symmetric in that dimensions
686
687 pSubPosHelper->doLogicScaling(NULL,&fUnclippedUpperYValue,NULL);
688
689 //calculate resulting width
690 double fCompleteHeight = bPositive ? fLogicPositiveYSum : fLogicNegativeYSum;
691 if( pPosHelper->isPercentY() )
692 fCompleteHeight = 1.0;
693 double fLogicBarWidth = fLogicBaseWidth;
694 double fTopHeight=approxSub(fCompleteHeight,fUpperYValue);
695 if(!bPositive)
696 fTopHeight=approxSub(fCompleteHeight,fLowerYValue);
697 double fLogicYStart = bPositive ? fLowerYValue : fUpperYValue;
698 double fMiddleHeight = fUpperYValue-fLowerYValue;
699 if(!bPositive)
700 fMiddleHeight*=-1.0;
701 double fLogicBarDepth = 0.5;
702 if(m_nDimension==3)
703 {
704 if( lcl_hasGeometry3DVariableWidth(nGeometry3D) && fCompleteHeight!=0.0 )
705 {
706 double fHeight = fCompleteHeight-fLowerYValue;
707 if(!bPositive)
708 fHeight = fCompleteHeight-fUpperYValue;
709 fLogicBarWidth = fLogicBaseWidth*fHeight/(fCompleteHeight);
710 if(fLogicBarWidth<=0.0)
711 fLogicBarWidth=fLogicBaseWidth;
712 fLogicBarDepth = fLogicBarDepth*fHeight/(fCompleteHeight);
713 if(fLogicBarDepth<=0.0)
714 fLogicBarDepth*=-1.0;
715 }
716 }
717
718 //better performance for big data
719 FormerBarPoint aFormerPoint( aSeriesFormerPointMap[pSeries] );
720 pPosHelper->setCoordinateSystemResolution( m_aCoordinateSystemResolution );
721 if( !pSeries->isAttributedDataPoint(nPointIndex)
722 &&
723 pPosHelper->isSameForGivenResolution( aFormerPoint.m_fX, aFormerPoint.m_fUpperY, aFormerPoint.m_fZ
724 , fLogicX, fUpperYValue, fLogicZ )
725 &&
726 pPosHelper->isSameForGivenResolution( aFormerPoint.m_fX, aFormerPoint.m_fLowerY, aFormerPoint.m_fZ
727 , fLogicX, fLowerYValue, fLogicZ )
728 )
729 {
730 nSkippedPoints++;
731 m_bPointsWereSkipped = true;
732 continue;
733 }
734 aSeriesFormerPointMap[pSeries] = FormerBarPoint(fLogicX,fUpperYValue,fLowerYValue,fLogicZ);
735 //
736
737 //
738 if( bDrawConnectionLines )
739 {
740 //store point information for connection lines
741
742 drawing::Position3D aLeftUpperPoint( fLogicX-fLogicBarWidth/2.0,fUnclippedUpperYValue,fLogicZ );
743 drawing::Position3D aRightUpperPoint( fLogicX+fLogicBarWidth/2.0,fUnclippedUpperYValue,fLogicZ );
744
745 if( isValidPosition(aLeftUpperPoint) )
746 AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aLeftUpperPoint );
747 if( isValidPosition(aRightUpperPoint) )
748 AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aRightUpperPoint );
749 }
750
751 if( bOnlyConnectionLinesForThisPoint )
752 continue;
753
754 //maybe additional possibility for performance improvement
755 //bool bCreateLineInsteadOfComplexGeometryDueToMissingSpace = false;
756 //pPosHelper->isSameForGivenResolution( fLogicX-fLogicBarWidth/2.0, fLowerYValue, fLogicZ
757 // , fLogicX+fLogicBarWidth/2.0, fLowerYValue, fLogicZ );
758
759 nCreatedPoints++;
760 //create partial point
761 if( !approxEqual(fLowerYValue,fUpperYValue) )
762 {
763 uno::Reference< drawing::XShape > xShape;
764 if( m_nDimension==3 )
765 {
766 drawing::Position3D aLogicBottom (fLogicX,fLogicYStart,fLogicZ);
767 drawing::Position3D aLogicLeftBottomFront (fLogicX+fLogicBarWidth/2.0,fLogicYStart,fLogicZ-fLogicBarDepth/2.0);
768 drawing::Position3D aLogicRightDeepTop (fLogicX-fLogicBarWidth/2.0,fLogicYStart+fMiddleHeight,fLogicZ+fLogicBarDepth/2.0);
769 drawing::Position3D aLogicTopTop (fLogicX,fLogicYStart+fMiddleHeight+fTopHeight,fLogicZ);
770
771 uno::Reference< XTransformation > xTransformation = pSubPosHelper->getTransformationScaledLogicToScene();
772
773 //transformation 3) -> 4)
774 drawing::Position3D aTransformedBottom ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicBottom) ) ) );
775 drawing::Position3D aTransformedLeftBottomFront ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicLeftBottomFront) ) ) );
776 drawing::Position3D aTransformedRightDeepTop ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicRightDeepTop) ) ) );
777 drawing::Position3D aTransformedTopTop ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicTopTop) ) ) );
778
779 drawing::Direction3D aSize = aTransformedRightDeepTop - aTransformedLeftBottomFront;
780 drawing::Direction3D aTopSize( aTransformedTopTop - aTransformedRightDeepTop );
781 fTopHeight = aTopSize.DirectionY;
782
783 sal_Int32 nRotateZAngleHundredthDegree = 0;
784 if( pPosHelper->isSwapXAndY() )
785 {
786 fTopHeight = aTopSize.DirectionX;
787 nRotateZAngleHundredthDegree = 90*100;
788 aSize = drawing::Direction3D(aSize.DirectionY,aSize.DirectionX,aSize.DirectionZ);
789 }
790
791 if( aSize.DirectionX < 0 )
792 aSize.DirectionX *= -1.0;
793 if( aSize.DirectionZ < 0 )
794 aSize.DirectionZ *= -1.0;
795 if( fTopHeight < 0 )
796 fTopHeight *= -1.0;
797
798 xShape = createDataPoint3D_Bar(
799 xPointGroupShape_Shapes, aTransformedBottom, aSize, fTopHeight, nRotateZAngleHundredthDegree
800 , xDataPointProperties, nGeometry3D );
801 }
802 else //m_nDimension!=3
803 {
804 //if( bCreateLineInsteadOfComplexGeometryDueToMissingSpace )
805 //{
806 // drawing::PolyPolygonShape3D aPoly;
807 // drawing::Position3D aUpperPoint( fLogicX,fUpperYValue,fLogicZ );
808 // drawing::Position3D aLowerPoint( fLogicX,fLowerYValue,fLogicZ );
809
810 // AddPointToPoly( aPoly, aUpperPoint );
811 // AddPointToPoly( aPoly, aLowerPoint );
812
813 // VLineProperties aLineProperties;
814 // aLineProperties.initFromPropertySet( xDataPointProperties, true /*bUseSeriesPropertyNames*/ );
815 // if( !aLineProperties.isLineVisible() )
816 // {
817 // //todo
818 // //aLineProperties.Color =
819 // }
820
821 // xShape = m_pShapeFactory->createLine2D( xPointGroupShape_Shapes
822 // , PolyToPointSequence(aPoly), &aLineProperties );
823 //}
824
825 drawing::PolyPolygonShape3D aPoly;
826 drawing::Position3D aLeftUpperPoint( fLogicX-fLogicBarWidth/2.0,fUpperYValue,fLogicZ );
827 drawing::Position3D aRightUpperPoint( fLogicX+fLogicBarWidth/2.0,fUpperYValue,fLogicZ );
828
829 AddPointToPoly( aPoly, drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) );
830 AddPointToPoly( aPoly, drawing::Position3D( fLogicX+fLogicBarWidth/2.0,fLowerYValue,fLogicZ) );
831 AddPointToPoly( aPoly, aRightUpperPoint );
832 AddPointToPoly( aPoly, aLeftUpperPoint );
833 AddPointToPoly( aPoly, drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) );
834 pPosHelper->transformScaledLogicToScene( aPoly );
835 xShape = m_pShapeFactory->createArea2D( xPointGroupShape_Shapes, aPoly );
836 this->setMappedProperties( xShape, xDataPointProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
837 }
838 //set name/classified ObjectID (CID)
839 ShapeFactory::setShapeName(xShape
840 , ObjectIdentifier::createPointCID(
841 (*aSeriesIter)->getPointCID_Stub(),nPointIndex) );
842 }
843
844 //create error bar
845 createErrorBar_Y( aUnscaledLogicPosition, **aSeriesIter, nPointIndex, m_xLogicTarget, &fLogicX );
846
847 //------------
848 //create data point label
849 if( (**aSeriesIter).getDataPointLabelIfLabel(nPointIndex) )
850 {
851 double fLogicSum = aLogicYSumMap[nAttachedAxisIndex];
852
853 LabelAlignment eAlignment(LABEL_ALIGN_CENTER);
854 sal_Int32 nLabelPlacement = pSeries->getLabelPlacement( nPointIndex, m_xChartTypeModel, m_nDimension, pPosHelper->isSwapXAndY() );
855
856 double fLowerBarDepth = fLogicBarDepth;
857 double fUpperBarDepth = fLogicBarDepth;
858 {
859 double fOuterBarDepth = fLogicBarDepth;
860 if( lcl_hasGeometry3DVariableWidth(nGeometry3D) && fCompleteHeight!=0.0 )
861 {
862 fOuterBarDepth = fLogicBarDepth * (fTopHeight)/(fabs(fCompleteHeight));
863 fLowerBarDepth = (fBaseValue < fUpperYValue) ? fabs(fLogicBarDepth) : fabs(fOuterBarDepth);
864 fUpperBarDepth = (fBaseValue < fUpperYValue) ? fabs(fOuterBarDepth) : fabs(fLogicBarDepth);
865 }
866 }
867
868 awt::Point aScreenPosition2D( this->getLabelScreenPositionAndAlignment(
869 eAlignment, nLabelPlacement
870 , fLogicX, fLowerYValue, fUpperYValue, fLogicZ
871 , fLowerBarDepth, fUpperBarDepth, fBaseValue, pPosHelper ));
872 sal_Int32 nOffset = 0;
873 if(LABEL_ALIGN_CENTER!=eAlignment)
874 {
875 nOffset = 100;//add some spacing //@todo maybe get more intelligent values
876 if( m_nDimension == 3 )
877 nOffset = 260;
878 }
879 this->createDataLabel( xTextTarget, **aSeriesIter, nPointIndex
880 , fLogicValueForLabeDisplay, fLogicSum, aScreenPosition2D, eAlignment, nOffset );
881 }
882
883 }//end iteration through partial points
884
885 //remove PointGroupShape if empty
886 // if(!xPointGroupShape_Shapes->getCount())
887 // xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shape);
888 }//next series in x slot (next y slot)
889 }//next x slot
890 }//next z slot
891 }//next category
892 //=============================================================================
893 //=============================================================================
894 //=============================================================================
895 if( bDrawConnectionLines )
896 {
897 ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin();
898 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
899 //=============================================================================
900 for( sal_Int32 nZ=1; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ )
901 {
902 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
903 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
904
905 BarPositionHelper* pPosHelper = m_pMainPosHelper;
906 if( aXSlotIter != aXSlotEnd )
907 {
908 sal_Int32 nAttachedAxisIndex = aXSlotIter->getAttachedAxisIndexForFirstSeries();
909 //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot
910 pPosHelper = dynamic_cast<BarPositionHelper*>(&( this->getPlottingPositionHelper( nAttachedAxisIndex ) ) );
911 if(!pPosHelper)
912 pPosHelper = m_pMainPosHelper;
913 }
914 PlotterBase::m_pPosHelper = pPosHelper;
915
916 //=============================================================================
917 //iterate through all x slots in this category
918 for( double fSlotX=0; aXSlotIter != aXSlotEnd; aXSlotIter++, fSlotX+=1.0 )
919 {
920 ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
921
922 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin();
923 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end();
924 aSeriesIter = pSeriesList->begin();
925 //=============================================================================
926 //iterate through all series in this x slot
927 for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ )
928 {
929 VDataSeries* pSeries( *aSeriesIter );
930 if(!pSeries)
931 continue;
932 drawing::PolyPolygonShape3D* pSeriesPoly = &pSeries->m_aPolyPolygonShape3D;
933 if(!ShapeFactory::hasPolygonAnyLines(*pSeriesPoly))
934 continue;
935
936 drawing::PolyPolygonShape3D aPoly;
937 Clipping::clipPolygonAtRectangle( *pSeriesPoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
938
939 if(!ShapeFactory::hasPolygonAnyLines(aPoly))
940 continue;
941
942 //transformation 3) -> 4)
943 pPosHelper->transformScaledLogicToScene( aPoly );
944
945 uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes(
946 getSeriesGroupShape(*aSeriesIter, xSeriesTarget) );
947 uno::Reference< drawing::XShape > xShape( m_pShapeFactory->createLine2D(
948 xSeriesGroupShape_Shapes, PolyToPointSequence( aPoly ) ) );
949 this->setMappedProperties( xShape, pSeries->getPropertiesOfSeries()
950 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
951 }
952 }
953 }
954 }
955
956 /* @todo remove series shapes if empty
957 //remove and delete point-group-shape if empty
958 if(!xSeriesGroupShape_Shapes->getCount())
959 {
960 (*aSeriesIter)->m_xShape.set(NULL);
961 m_xLogicTarget->remove(xSeriesGroupShape_Shape);
962 }
963 */
964
965 //remove and delete series-group-shape if empty
966
967 //... todo
968
969 OSL_TRACE( "\nPPPPPPPPP<<<<<<<<<<<< bar chart :: createShapes():: skipped points: %d created points: %d", nSkippedPoints, nCreatedPoints );
970 }
971
972 //.............................................................................
973 } //namespace chart
974 //.............................................................................
975