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 "AreaChart.hxx"
29 #include "PlottingPositionHelper.hxx"
30 #include "ShapeFactory.hxx"
31 //#include "chartview/servicenames_charttypes.hxx"
32 #include "CommonConverters.hxx"
33 #include "macros.hxx"
34 #include "ViewDefines.hxx"
35 #include "ObjectIdentifier.hxx"
36 #include "Splines.hxx"
37 #include "ChartTypeHelper.hxx"
38 #include "LabelPositionHelper.hxx"
39 #include "Clipping.hxx"
40 #include "Stripe.hxx"
41 #include "PolarLabelPositionHelper.hxx"
42 #include "DateHelper.hxx"
43
44 #include <com/sun/star/chart2/Symbol.hpp>
45 #include <com/sun/star/chart/DataLabelPlacement.hpp>
46 #include <com/sun/star/chart/MissingValueTreatment.hpp>
47
48 #include <tools/debug.hxx>
49 #include <editeng/unoprnms.hxx>
50 #include <rtl/math.hxx>
51
52 #include <com/sun/star/drawing/DoubleSequence.hpp>
53 #include <com/sun/star/drawing/NormalsKind.hpp>
54 #include <com/sun/star/lang/XServiceName.hpp>
55
56 //.............................................................................
57 namespace chart
58 {
59 //.............................................................................
60 using namespace ::com::sun::star;
61 using namespace ::rtl::math;
62 using namespace ::com::sun::star::chart2;
63
64 //-----------------------------------------------------------------------------
65 //-----------------------------------------------------------------------------
66 //-----------------------------------------------------------------------------
67
AreaChart(const uno::Reference<XChartType> & xChartTypeModel,sal_Int32 nDimensionCount,bool bCategoryXAxis,bool bNoArea,PlottingPositionHelper * pPlottingPositionHelper,bool bConnectLastToFirstPoint,bool bExpandIfValuesCloseToBorder,sal_Int32 nKeepAspectRatio,const drawing::Direction3D & rAspectRatio)68 AreaChart::AreaChart( const uno::Reference<XChartType>& xChartTypeModel
69 , sal_Int32 nDimensionCount
70 , bool bCategoryXAxis
71 , bool bNoArea
72 , PlottingPositionHelper* pPlottingPositionHelper
73 , bool bConnectLastToFirstPoint
74 , bool bExpandIfValuesCloseToBorder
75 , sal_Int32 nKeepAspectRatio
76 , const drawing::Direction3D& rAspectRatio
77 )
78 : VSeriesPlotter( xChartTypeModel, nDimensionCount, bCategoryXAxis )
79 , m_pMainPosHelper(pPlottingPositionHelper)
80 , m_bArea(!bNoArea)
81 , m_bLine(bNoArea)
82 , m_bSymbol( ChartTypeHelper::isSupportingSymbolProperties(xChartTypeModel,nDimensionCount) )
83 , m_bIsPolarCooSys( bConnectLastToFirstPoint )
84 , m_bConnectLastToFirstPoint( bConnectLastToFirstPoint )
85 , m_bExpandIfValuesCloseToBorder( bExpandIfValuesCloseToBorder )
86 , m_nKeepAspectRatio(nKeepAspectRatio)
87 , m_aGivenAspectRatio(rAspectRatio)
88 , m_eCurveStyle(CurveStyle_LINES)
89 , m_nCurveResolution(20)
90 , m_nSplineOrder(3)
91 , m_xSeriesTarget(0)
92 , m_xErrorBarTarget(0)
93 , m_xTextTarget(0)
94 , m_xRegressionCurveEquationTarget(0)
95 {
96 if( !m_pMainPosHelper )
97 m_pMainPosHelper = new PlottingPositionHelper();
98 if( m_pMainPosHelper )
99 {
100 m_pMainPosHelper->AllowShiftXAxisPos(true);
101 m_pMainPosHelper->AllowShiftZAxisPos(true);
102 }
103 PlotterBase::m_pPosHelper = m_pMainPosHelper;
104 VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper;
105
106 try
107 {
108 if( m_xChartTypeModelProps.is() )
109 {
110 m_xChartTypeModelProps->getPropertyValue( C2U( "CurveStyle" ) ) >>= m_eCurveStyle;
111 m_xChartTypeModelProps->getPropertyValue( C2U( "CurveResolution" ) ) >>= m_nCurveResolution;
112 m_xChartTypeModelProps->getPropertyValue( C2U( "SplineOrder" ) ) >>= m_nSplineOrder;
113 }
114 }
115 catch( uno::Exception& e )
116 {
117 //the above properties are not supported by all charttypes supported by this class (e.g. area or net chart)
118 //in that cases this exception is ok
119 e.Context.is();//to have debug information without compilation warnings
120 }
121 }
122
~AreaChart()123 AreaChart::~AreaChart()
124 {
125 delete m_pMainPosHelper;
126 }
127
getMaximumX()128 double AreaChart::getMaximumX()
129 {
130 double fMax = VSeriesPlotter::getMaximumX();
131 if( m_bCategoryXAxis && m_bIsPolarCooSys )//the angle axis in net charts needs a different autoscaling
132 fMax += 1.0;
133 return fMax;
134 }
135
isExpandIfValuesCloseToBorder(sal_Int32 nDimensionIndex)136 bool AreaChart::isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex )
137 {
138 return m_bExpandIfValuesCloseToBorder &&
139 VSeriesPlotter::isExpandIfValuesCloseToBorder( nDimensionIndex );
140 }
141
isSeperateStackingForDifferentSigns(sal_Int32)142 bool AreaChart::isSeperateStackingForDifferentSigns( sal_Int32 /*nDimensionIndex*/ )
143 {
144 // no separate stacking in all types of line/area charts
145 return false;
146 }
147
148 //-----------------------------------------------------------------
149
getLegendSymbolStyle()150 LegendSymbolStyle AreaChart::getLegendSymbolStyle()
151 {
152 if( m_bArea || m_nDimension == 3 )
153 return LegendSymbolStyle_BOX;
154 return LegendSymbolStyle_LINE;
155 }
156
getExplicitSymbol(const VDataSeries & rSeries,sal_Int32 nPointIndex)157 uno::Any AreaChart::getExplicitSymbol( const VDataSeries& rSeries, sal_Int32 nPointIndex )
158 {
159 uno::Any aRet;
160
161 Symbol* pSymbolProperties = rSeries.getSymbolProperties( nPointIndex );
162 if( pSymbolProperties )
163 {
164 aRet = uno::makeAny(*pSymbolProperties);
165 }
166
167 return aRet;
168 }
169
170 //-----------------------------------------------------------------
171 // lang::XServiceInfo
172 //-----------------------------------------------------------------
173 /*
174 APPHELPER_XSERVICEINFO_IMPL(AreaChart,CHART2_VIEW_AREACHART_SERVICE_IMPLEMENTATION_NAME)
175
176 uno::Sequence< rtl::OUString > AreaChart
177 ::getSupportedServiceNames_Static()
178 {
179 uno::Sequence< rtl::OUString > aSNS( 1 );
180 aSNS.getArray()[ 0 ] = CHART2_VIEW_AREACHART_SERVICE_NAME;
181 return aSNS;
182 }
183 */
getPreferredDiagramAspectRatio() const184 drawing::Direction3D AreaChart::getPreferredDiagramAspectRatio() const
185 {
186 if( m_nKeepAspectRatio == 1 )
187 return m_aGivenAspectRatio;
188 drawing::Direction3D aRet(1,-1,1);
189 if( m_nDimension == 2 )
190 aRet = drawing::Direction3D(-1,-1,-1);
191 else
192 {
193 drawing::Direction3D aScale( m_pPosHelper->getScaledLogicWidth() );
194 aRet.DirectionZ = aScale.DirectionZ*0.2;
195 if(aRet.DirectionZ>1.0)
196 aRet.DirectionZ=1.0;
197 if(aRet.DirectionZ>10)
198 aRet.DirectionZ=10;
199 }
200 return aRet;
201 }
202
keepAspectRatio() const203 bool AreaChart::keepAspectRatio() const
204 {
205 if( m_nKeepAspectRatio == 0 )
206 return false;
207 if( m_nKeepAspectRatio == 1 )
208 return true;
209 if( m_nDimension == 2 )
210 {
211 if( !m_bSymbol )
212 return false;
213 }
214 return true;
215 }
216
addSeries(VDataSeries * pSeries,sal_Int32 zSlot,sal_Int32 xSlot,sal_Int32 ySlot)217 void AreaChart::addSeries( VDataSeries* pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot )
218 {
219 if( m_bArea && !m_bIsPolarCooSys && pSeries )
220 {
221 sal_Int32 nMissingValueTreatment = pSeries->getMissingValueTreatment();
222 if( nMissingValueTreatment == ::com::sun::star::chart::MissingValueTreatment::LEAVE_GAP )
223 pSeries->setMissingValueTreatment( ::com::sun::star::chart::MissingValueTreatment::USE_ZERO );
224 }
225 if( m_nDimension == 3 && !m_bCategoryXAxis )
226 {
227 //3D xy always deep
228 DBG_ASSERT( zSlot==-1,"3D xy charts should be deep stacked in model also" );
229 zSlot=-1;
230 xSlot=0;
231 ySlot=0;
232 }
233 VSeriesPlotter::addSeries( pSeries, zSlot, xSlot, ySlot );
234 }
235
lcl_removeDuplicatePoints(drawing::PolyPolygonShape3D & rPolyPoly,PlottingPositionHelper & rPosHelper)236 void lcl_removeDuplicatePoints( drawing::PolyPolygonShape3D& rPolyPoly, PlottingPositionHelper& rPosHelper )
237 {
238 sal_Int32 nPolyCount = rPolyPoly.SequenceX.getLength();
239 if(!nPolyCount)
240 return;
241
242 drawing::PolyPolygonShape3D aTmp;
243 aTmp.SequenceX.realloc(nPolyCount);
244 aTmp.SequenceY.realloc(nPolyCount);
245 aTmp.SequenceZ.realloc(nPolyCount);
246
247 for( sal_Int32 nPolygonIndex = 0; nPolygonIndex<nPolyCount; nPolygonIndex++ )
248 {
249 drawing::DoubleSequence* pOuterSourceX = &rPolyPoly.SequenceX.getArray()[nPolygonIndex];
250 drawing::DoubleSequence* pOuterSourceY = &rPolyPoly.SequenceY.getArray()[nPolygonIndex];
251 drawing::DoubleSequence* pOuterSourceZ = &rPolyPoly.SequenceZ.getArray()[nPolygonIndex];
252
253 drawing::DoubleSequence* pOuterTargetX = &aTmp.SequenceX.getArray()[nPolygonIndex];
254 drawing::DoubleSequence* pOuterTargetY = &aTmp.SequenceY.getArray()[nPolygonIndex];
255 drawing::DoubleSequence* pOuterTargetZ = &aTmp.SequenceZ.getArray()[nPolygonIndex];
256
257 sal_Int32 nPointCount = pOuterSourceX->getLength();
258 if( !nPointCount )
259 continue;
260
261 pOuterTargetX->realloc(nPointCount);
262 pOuterTargetY->realloc(nPointCount);
263 pOuterTargetZ->realloc(nPointCount);
264
265 double* pSourceX = pOuterSourceX->getArray();
266 double* pSourceY = pOuterSourceY->getArray();
267 double* pSourceZ = pOuterSourceZ->getArray();
268
269 double* pTargetX = pOuterTargetX->getArray();
270 double* pTargetY = pOuterTargetY->getArray();
271 double* pTargetZ = pOuterTargetZ->getArray();
272
273 //copy first point
274 *pTargetX=*pSourceX++;
275 *pTargetY=*pSourceY++;
276 *pTargetZ=*pSourceZ++;
277 sal_Int32 nTargetPointCount=1;
278
279 for( sal_Int32 nSource=1; nSource<nPointCount; nSource++ )
280 {
281 if( !rPosHelper.isSameForGivenResolution( *pTargetX, *pTargetY, *pTargetZ
282 , *pSourceX, *pSourceY, *pSourceZ ) )
283 {
284 pTargetX++; pTargetY++; pTargetZ++;
285 *pTargetX=*pSourceX;
286 *pTargetY=*pSourceY;
287 *pTargetZ=*pSourceZ;
288 nTargetPointCount++;
289 }
290 pSourceX++; pSourceY++; pSourceZ++;
291 }
292
293 //free unused space
294 if( nTargetPointCount<nPointCount )
295 {
296 pOuterTargetX->realloc(nTargetPointCount);
297 pOuterTargetY->realloc(nTargetPointCount);
298 pOuterTargetZ->realloc(nTargetPointCount);
299 }
300
301 pOuterSourceX->realloc(0);
302 pOuterSourceY->realloc(0);
303 pOuterSourceZ->realloc(0);
304 }
305
306 //free space
307 rPolyPoly.SequenceX.realloc(nPolyCount);
308 rPolyPoly.SequenceY.realloc(nPolyCount);
309 rPolyPoly.SequenceZ.realloc(nPolyCount);
310
311 rPolyPoly=aTmp;
312 }
313
impl_createLine(VDataSeries * pSeries,drawing::PolyPolygonShape3D * pSeriesPoly,PlottingPositionHelper * pPosHelper)314 bool AreaChart::impl_createLine( VDataSeries* pSeries
315 , drawing::PolyPolygonShape3D* pSeriesPoly
316 , PlottingPositionHelper* pPosHelper )
317 {
318 //return true if a line was created successfully
319 uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShapeBackChild(pSeries, m_xSeriesTarget);
320
321 drawing::PolyPolygonShape3D aPoly;
322 if(CurveStyle_CUBIC_SPLINES==m_eCurveStyle)
323 {
324 drawing::PolyPolygonShape3D aSplinePoly;
325 SplineCalculater::CalculateCubicSplines( *pSeriesPoly, aSplinePoly, m_nCurveResolution );
326 lcl_removeDuplicatePoints( aSplinePoly, *pPosHelper );
327 Clipping::clipPolygonAtRectangle( aSplinePoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
328 }
329 else if(CurveStyle_B_SPLINES==m_eCurveStyle)
330 {
331 drawing::PolyPolygonShape3D aSplinePoly;
332 SplineCalculater::CalculateBSplines( *pSeriesPoly, aSplinePoly, m_nCurveResolution, m_nSplineOrder );
333 lcl_removeDuplicatePoints( aSplinePoly, *pPosHelper );
334 Clipping::clipPolygonAtRectangle( aSplinePoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
335 }
336 else
337 {
338 bool bIsClipped = false;
339 if( m_bConnectLastToFirstPoint && !ShapeFactory::isPolygonEmptyOrSinglePoint(*pSeriesPoly) )
340 {
341 // do NOT connect last and first point, if one is NAN, and NAN handling is NAN_AS_GAP
342 double fFirstY = pSeries->getYValue( 0 );
343 double fLastY = pSeries->getYValue( VSeriesPlotter::getPointCount() - 1 );
344 if( (pSeries->getMissingValueTreatment() != ::com::sun::star::chart::MissingValueTreatment::LEAVE_GAP)
345 || (::rtl::math::isFinite( fFirstY ) && ::rtl::math::isFinite( fLastY )) )
346 {
347 // connect last point in last polygon with first point in first polygon
348 ::basegfx::B2DRectangle aScaledLogicClipDoubleRect( pPosHelper->getScaledLogicClipDoubleRect() );
349 drawing::PolyPolygonShape3D aTmpPoly(*pSeriesPoly);
350 drawing::Position3D aLast(aScaledLogicClipDoubleRect.getMaxX(),aTmpPoly.SequenceY[0][0],aTmpPoly.SequenceZ[0][0]);
351 // add connector line to last polygon
352 AddPointToPoly( aTmpPoly, aLast, pSeriesPoly->SequenceX.getLength() - 1 );
353 Clipping::clipPolygonAtRectangle( aTmpPoly, aScaledLogicClipDoubleRect, aPoly );
354 bIsClipped = true;
355 }
356 }
357
358 if( !bIsClipped )
359 Clipping::clipPolygonAtRectangle( *pSeriesPoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly );
360 }
361
362 if(!ShapeFactory::hasPolygonAnyLines(aPoly))
363 return false;
364
365 //transformation 3) -> 4)
366 pPosHelper->transformScaledLogicToScene( aPoly );
367
368 //create line:
369 uno::Reference< drawing::XShape > xShape(NULL);
370 if(m_nDimension==3)
371 {
372 double fDepth = this->getTransformedDepth();
373 sal_Int32 nPolyCount = aPoly.SequenceX.getLength();
374 for(sal_Int32 nPoly=0;nPoly<nPolyCount;nPoly++)
375 {
376 sal_Int32 nPointCount = aPoly.SequenceX[nPoly].getLength();
377 for(sal_Int32 nPoint=0;nPoint<nPointCount-1;nPoint++)
378 {
379 drawing::Position3D aPoint1, aPoint2;
380 aPoint1.PositionX = aPoly.SequenceX[nPoly][nPoint+1];
381 aPoint1.PositionY = aPoly.SequenceY[nPoly][nPoint+1];
382 aPoint1.PositionZ = aPoly.SequenceZ[nPoly][nPoint+1];
383
384 aPoint2.PositionX = aPoly.SequenceX[nPoly][nPoint];
385 aPoint2.PositionY = aPoly.SequenceY[nPoly][nPoint];
386 aPoint2.PositionZ = aPoly.SequenceZ[nPoly][nPoint];
387
388 Stripe aStripe( aPoint1, aPoint2, fDepth );
389
390 m_pShapeFactory->createStripe(xSeriesGroupShape_Shapes
391 , Stripe( aPoint1, aPoint2, fDepth )
392 , pSeries->getPropertiesOfSeries(), PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), true, 1 );
393 }
394 }
395 }
396 else //m_nDimension!=3
397 {
398 xShape = m_pShapeFactory->createLine2D( xSeriesGroupShape_Shapes
399 , PolyToPointSequence( aPoly ) );
400 this->setMappedProperties( xShape
401 , pSeries->getPropertiesOfSeries()
402 , PropertyMapper::getPropertyNameMapForLineSeriesProperties() );
403 //because of this name this line will be used for marking
404 m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") );
405 }
406 return true;
407 }
408
impl_createArea(VDataSeries * pSeries,drawing::PolyPolygonShape3D * pSeriesPoly,drawing::PolyPolygonShape3D * pPreviousSeriesPoly,PlottingPositionHelper * pPosHelper)409 bool AreaChart::impl_createArea( VDataSeries* pSeries
410 , drawing::PolyPolygonShape3D* pSeriesPoly
411 , drawing::PolyPolygonShape3D* pPreviousSeriesPoly
412 , PlottingPositionHelper* pPosHelper )
413 {
414 //return true if an area was created successfully
415
416 uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShapeBackChild(pSeries, m_xSeriesTarget);
417 double zValue = pSeries->m_fLogicZPos;
418
419 drawing::PolyPolygonShape3D aPoly( *pSeriesPoly );
420 //add second part to the polygon (grounding points or previous series points)
421 if( m_bConnectLastToFirstPoint && !ShapeFactory::isPolygonEmptyOrSinglePoint(*pSeriesPoly) )
422 {
423 if( pPreviousSeriesPoly )
424 addPolygon( aPoly, *pPreviousSeriesPoly );
425 }
426 else if(!pPreviousSeriesPoly)
427 {
428 double fMinX = pSeries->m_fLogicMinX;
429 double fMaxX = pSeries->m_fLogicMaxX;
430 double fY = pPosHelper->getBaseValueY();//logic grounding
431 if( m_nDimension==3 )
432 fY = pPosHelper->getLogicMinY();
433
434 //clip to scale
435 if(fMaxX<pPosHelper->getLogicMinX() || fMinX>pPosHelper->getLogicMaxX())
436 return false;//no visible shape needed
437 pPosHelper->clipLogicValues( &fMinX, &fY, 0 );
438 pPosHelper->clipLogicValues( &fMaxX, 0, 0 );
439
440 //apply scaling
441 {
442 pPosHelper->doLogicScaling( &fMinX, &fY, &zValue );
443 pPosHelper->doLogicScaling( &fMaxX, 0, 0 );
444 }
445
446 AddPointToPoly( aPoly, drawing::Position3D( fMaxX,fY,zValue) );
447 AddPointToPoly( aPoly, drawing::Position3D( fMinX,fY,zValue) );
448 }
449 else
450 {
451 appendPoly( aPoly, *pPreviousSeriesPoly );
452 }
453 ShapeFactory::closePolygon(aPoly);
454
455 //apply clipping
456 {
457 drawing::PolyPolygonShape3D aClippedPoly;
458 Clipping::clipPolygonAtRectangle( aPoly, pPosHelper->getScaledLogicClipDoubleRect(), aClippedPoly, false );
459 ShapeFactory::closePolygon(aClippedPoly); //again necessary after clipping
460 aPoly = aClippedPoly;
461 }
462
463 if(!ShapeFactory::hasPolygonAnyLines(aPoly))
464 return false;
465
466 //transformation 3) -> 4)
467 pPosHelper->transformScaledLogicToScene( aPoly );
468
469 //create area:
470 uno::Reference< drawing::XShape > xShape(NULL);
471 if(m_nDimension==3)
472 {
473 xShape = m_pShapeFactory->createArea3D( xSeriesGroupShape_Shapes
474 , aPoly, this->getTransformedDepth() );
475 }
476 else //m_nDimension!=3
477 {
478 xShape = m_pShapeFactory->createArea2D( xSeriesGroupShape_Shapes
479 , aPoly );
480 }
481 this->setMappedProperties( xShape
482 , pSeries->getPropertiesOfSeries()
483 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
484 //because of this name this line will be used for marking
485 m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") );
486 return true;
487 }
488
impl_createSeriesShapes()489 void AreaChart::impl_createSeriesShapes()
490 {
491 //the polygon shapes for each series need to be created before
492
493 //iterate through all series again to create the series shapes
494 ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin();
495 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
496 //=============================================================================
497 for( sal_Int32 nZ=1; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ )
498 {
499 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
500 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
501
502 //=============================================================================
503 for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ )
504 {
505 ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
506
507 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin();
508 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end();
509 //=============================================================================
510
511 std::map< sal_Int32, drawing::PolyPolygonShape3D* > aPreviousSeriesPolyMap;//a PreviousSeriesPoly for each different nAttachedAxisIndex
512 drawing::PolyPolygonShape3D* pSeriesPoly = NULL;
513
514 //iterate through all series
515 for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ )
516 {
517 sal_Int32 nAttachedAxisIndex = (*aSeriesIter)->getAttachedAxisIndex();
518 PlottingPositionHelper* pPosHelper = &(this->getPlottingPositionHelper( nAttachedAxisIndex ));
519 if(!pPosHelper)
520 pPosHelper = m_pMainPosHelper;
521 PlotterBase::m_pPosHelper = pPosHelper;
522
523 createRegressionCurvesShapes( **aSeriesIter, m_xErrorBarTarget, m_xRegressionCurveEquationTarget,
524 m_pPosHelper->maySkipPointsInRegressionCalculation());
525
526 pSeriesPoly = &(*aSeriesIter)->m_aPolyPolygonShape3D;
527 if( m_bArea )
528 {
529 if( !impl_createArea( *aSeriesIter, pSeriesPoly, aPreviousSeriesPolyMap[nAttachedAxisIndex], pPosHelper ) )
530 continue;
531 }
532 if( m_bLine )
533 {
534 if( !impl_createLine( *aSeriesIter, pSeriesPoly, pPosHelper ) )
535 continue;
536 }
537 aPreviousSeriesPolyMap[nAttachedAxisIndex] = pSeriesPoly;
538 }//next series in x slot (next y slot)
539 }//next x slot
540 }//next z slot
541 }
542
543 namespace
544 {
545
lcl_reorderSeries(::std::vector<::std::vector<VDataSeriesGroup>> & rZSlots)546 void lcl_reorderSeries( ::std::vector< ::std::vector< VDataSeriesGroup > >& rZSlots )
547 {
548 ::std::vector< ::std::vector< VDataSeriesGroup > > aRet( rZSlots.size() );
549
550 ::std::vector< ::std::vector< VDataSeriesGroup > >::reverse_iterator aZIt( rZSlots.rbegin() );
551 ::std::vector< ::std::vector< VDataSeriesGroup > >::reverse_iterator aZEnd( rZSlots.rend() );
552 for( ; aZIt != aZEnd; ++aZIt )
553 {
554 ::std::vector< VDataSeriesGroup > aXSlot( aZIt->size() );
555
556 ::std::vector< VDataSeriesGroup >::reverse_iterator aXIt( aZIt->rbegin() );
557 ::std::vector< VDataSeriesGroup >::reverse_iterator aXEnd( aZIt->rend() );
558 for( ; aXIt != aXEnd; ++aXIt )
559 aXSlot.push_back(*aXIt);
560
561 aRet.push_back(aXSlot);
562 }
563
564 rZSlots.clear();
565 rZSlots = aRet;
566 }
567
568 }//anonymous namespace
569
570 //better performance for big data
571 struct FormerPoint
572 {
FormerPointchart::FormerPoint573 FormerPoint( double fX, double fY, double fZ )
574 : m_fX(fX), m_fY(fY), m_fZ(fZ)
575 {}
FormerPointchart::FormerPoint576 FormerPoint()
577 {
578 ::rtl::math::setNan( &m_fX );
579 ::rtl::math::setNan( &m_fY );
580 ::rtl::math::setNan( &m_fZ );
581 }
582
583 double m_fX;
584 double m_fY;
585 double m_fZ;
586 };
587
createShapes()588 void AreaChart::createShapes()
589 {
590 if( m_aZSlots.begin() == m_aZSlots.end() ) //no series
591 return;
592
593 if( m_nDimension == 2 && ( m_bArea || !m_bCategoryXAxis ) )
594 lcl_reorderSeries( m_aZSlots );
595
596 DBG_ASSERT(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"AreaChart is not proper initialized");
597 if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is()))
598 return;
599
600 //the text labels should be always on top of the other series shapes
601 //for area chart the error bars should be always on top of the other series shapes
602
603 //therefore create an own group for the texts and the error bars to move them to front
604 //(because the text group is created after the series group the texts are displayed on top)
605 m_xSeriesTarget = createGroupShape( m_xLogicTarget,rtl::OUString() );
606 if( m_bArea )
607 m_xErrorBarTarget = createGroupShape( m_xLogicTarget,rtl::OUString() );
608 else
609 m_xErrorBarTarget = m_xSeriesTarget;
610 m_xTextTarget = m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() );
611 m_xRegressionCurveEquationTarget = m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() );
612
613 //---------------------------------------------
614 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
615
616 //update/create information for current group
617 double fLogicZ = 1.0;//as defined
618
619 sal_Int32 nStartIndex = 0; // inclusive ;..todo get somehow from x scale
620 sal_Int32 nEndIndex = VSeriesPlotter::getPointCount();
621 if(nEndIndex<=0)
622 nEndIndex=1;
623
624 //better performance for big data
625 std::map< VDataSeries*, FormerPoint > aSeriesFormerPointMap;
626 m_bPointsWereSkipped = false;
627 sal_Int32 nSkippedPoints = 0;
628 sal_Int32 nCreatedPoints = 0;
629 //
630
631 //=============================================================================
632 //iterate through all x values per indices
633 for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; nIndex++ )
634 {
635 ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin();
636 const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
637
638 std::map< sal_Int32, double > aLogicYSumMap;//one for each different nAttachedAxisIndex
639 for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ )
640 {
641 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
642 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
643
644 //iterate through all x slots in this category to get 100percent sum
645 for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ )
646 {
647 ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
648 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin();
649 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end();
650
651 for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ )
652 {
653 VDataSeries* pSeries( *aSeriesIter );
654 if(!pSeries)
655 continue;
656
657 sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex();
658 if( aLogicYSumMap.find(nAttachedAxisIndex)==aLogicYSumMap.end() )
659 aLogicYSumMap[nAttachedAxisIndex]=0.0;
660
661 PlottingPositionHelper* pPosHelper = &(this->getPlottingPositionHelper( nAttachedAxisIndex ));
662 if(!pPosHelper)
663 pPosHelper = m_pMainPosHelper;
664 PlotterBase::m_pPosHelper = pPosHelper;
665
666 double fAdd = pSeries->getYValue( nIndex );
667 if( !::rtl::math::isNan(fAdd) && !::rtl::math::isInf(fAdd) )
668 aLogicYSumMap[nAttachedAxisIndex] += fabs( fAdd );
669 }
670 }
671 }
672
673 //=============================================================================
674 aZSlotIter = m_aZSlots.begin();
675 for( sal_Int32 nZ=1; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ )
676 {
677 ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin();
678 const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
679
680 //for the area chart there should be at most one x slot (no side by side stacking available)
681 //attention different: xSlots are always interpreted as independent areas one behind the other: @todo this doesn't work why not???
682 aXSlotIter = aZSlotIter->begin();
683 for( sal_Int32 nX=0; aXSlotIter != aXSlotEnd; aXSlotIter++, nX++ )
684 {
685 ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
686 ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin();
687 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end();
688
689 std::map< sal_Int32, double > aLogicYForNextSeriesMap;//one for each different nAttachedAxisIndex
690 //=============================================================================
691 //iterate through all series
692 for( sal_Int32 nSeriesIndex = 0; aSeriesIter != aSeriesEnd; aSeriesIter++, nSeriesIndex++ )
693 {
694 VDataSeries* pSeries( *aSeriesIter );
695 if(!pSeries)
696 continue;
697
698 /* #i70133# ignore points outside of series length in standard area
699 charts. Stacked area charts will use missing points as zeros. In
700 standard charts, pSeriesList contains only one series. */
701 if( m_bArea && (pSeriesList->size() == 1) && (nIndex >= (*aSeriesIter)->getTotalPointCount()) )
702 continue;
703
704 uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShapeFrontChild(*aSeriesIter, m_xSeriesTarget);
705
706 sal_Int32 nAttachedAxisIndex = (*aSeriesIter)->getAttachedAxisIndex();
707 PlottingPositionHelper* pPosHelper = &(this->getPlottingPositionHelper( nAttachedAxisIndex ));
708 if(!pPosHelper)
709 pPosHelper = m_pMainPosHelper;
710 PlotterBase::m_pPosHelper = pPosHelper;
711
712 if(m_nDimension==3)
713 fLogicZ = nZ+0.5;
714 (*aSeriesIter)->m_fLogicZPos = fLogicZ;
715
716 //collect data point information (logic coordinates, style ):
717 double fLogicX = (*aSeriesIter)->getXValue(nIndex);
718 if( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() )
719 fLogicX = DateHelper::RasterizeDateValue( fLogicX, m_aNullDate, m_nTimeResolution );
720 double fLogicY = (*aSeriesIter)->getYValue(nIndex);
721
722 if( m_bIsPolarCooSys && m_bArea &&
723 ( ::rtl::math::isNan(fLogicY) || ::rtl::math::isInf(fLogicY) ) )
724 {
725 if( (*aSeriesIter)->getMissingValueTreatment() == ::com::sun::star::chart::MissingValueTreatment::LEAVE_GAP )
726 {
727 if( pSeriesList->size() == 1 || nSeriesIndex == 0 )
728 {
729 fLogicY = pPosHelper->getLogicMinY();
730 if( !pPosHelper->isMathematicalOrientationY() )
731 fLogicY = pPosHelper->getLogicMaxY();
732 }
733 else
734 fLogicY = 0.0;
735 }
736 }
737
738 if( m_nDimension==3 && m_bArea && pSeriesList->size()!=1 )
739 fLogicY = fabs( fLogicY );
740
741 if( pPosHelper->isPercentY() && !::rtl::math::approxEqual( aLogicYSumMap[nAttachedAxisIndex], 0.0 ) )
742 {
743 fLogicY = fabs( fLogicY )/aLogicYSumMap[nAttachedAxisIndex];
744 }
745
746 if( ::rtl::math::isNan(fLogicX) || ::rtl::math::isInf(fLogicX)
747 || ::rtl::math::isNan(fLogicY) || ::rtl::math::isInf(fLogicY)
748 || ::rtl::math::isNan(fLogicZ) || ::rtl::math::isInf(fLogicZ) )
749 {
750 if( (*aSeriesIter)->getMissingValueTreatment() == ::com::sun::star::chart::MissingValueTreatment::LEAVE_GAP )
751 {
752 drawing::PolyPolygonShape3D& rPolygon = (*aSeriesIter)->m_aPolyPolygonShape3D;
753 sal_Int32& rIndex = (*aSeriesIter)->m_nPolygonIndex;
754 if( 0<= rIndex && rIndex < rPolygon.SequenceX.getLength() )
755 {
756 if( rPolygon.SequenceX[ rIndex ].getLength() )
757 rIndex++; //start a new polygon for the next point if the current poly is not empty
758 }
759 }
760 continue;
761 }
762
763 if( aLogicYForNextSeriesMap.find(nAttachedAxisIndex) == aLogicYForNextSeriesMap.end() )
764 aLogicYForNextSeriesMap[nAttachedAxisIndex] = 0.0;
765
766 double fLogicValueForLabeDisplay = fLogicY;
767
768 fLogicY += aLogicYForNextSeriesMap[nAttachedAxisIndex];
769 aLogicYForNextSeriesMap[nAttachedAxisIndex] = fLogicY;
770
771 bool bIsVisible = pPosHelper->isLogicVisible( fLogicX, fLogicY, fLogicZ );
772
773 //remind minimal and maximal x values for area 'grounding' points
774 //only for filled area
775 {
776 double& rfMinX = (*aSeriesIter)->m_fLogicMinX;
777 if(!nIndex||fLogicX<rfMinX)
778 rfMinX=fLogicX;
779 double& rfMaxX = (*aSeriesIter)->m_fLogicMaxX;
780 if(!nIndex||fLogicX>rfMaxX)
781 rfMaxX=fLogicX;
782 }
783
784 drawing::Position3D aUnscaledLogicPosition( fLogicX, fLogicY, fLogicZ );
785 drawing::Position3D aScaledLogicPosition(aUnscaledLogicPosition);
786 pPosHelper->doLogicScaling( aScaledLogicPosition );
787
788 //transformation 3) -> 4)
789 drawing::Position3D aScenePosition( pPosHelper->transformLogicToScene( fLogicX,fLogicY,fLogicZ, false ) );
790
791 //better performance for big data
792 FormerPoint aFormerPoint( aSeriesFormerPointMap[pSeries] );
793 pPosHelper->setCoordinateSystemResolution( m_aCoordinateSystemResolution );
794 if( !pSeries->isAttributedDataPoint(nIndex)
795 &&
796 pPosHelper->isSameForGivenResolution( aFormerPoint.m_fX, aFormerPoint.m_fY, aFormerPoint.m_fZ
797 , aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY, aScaledLogicPosition.PositionZ ) )
798 {
799 nSkippedPoints++;
800 m_bPointsWereSkipped = true;
801 continue;
802 }
803 aSeriesFormerPointMap[pSeries] = FormerPoint(aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY, aScaledLogicPosition.PositionZ);
804 //
805
806 //store point information for series polygon
807 //for area and/or line (symbols only do not need this)
808 if( isValidPosition(aScaledLogicPosition) )
809 {
810 AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aScaledLogicPosition, (*aSeriesIter)->m_nPolygonIndex );
811
812 //prepare clipping for filled net charts
813 if( !bIsVisible && m_bIsPolarCooSys && m_bArea )
814 {
815 drawing::Position3D aClippedPos(aScaledLogicPosition);
816 pPosHelper->clipScaledLogicValues( 0, &aClippedPos.PositionY, 0 );
817 if( pPosHelper->isLogicVisible( aClippedPos.PositionX, aClippedPos.PositionY, aClippedPos.PositionZ ) )
818 {
819 AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aClippedPos, (*aSeriesIter)->m_nPolygonIndex );
820 AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aScaledLogicPosition, (*aSeriesIter)->m_nPolygonIndex );
821 }
822 }
823 }
824
825 //create a single datapoint if point is visible
826 //apply clipping:
827 if( !bIsVisible )
828 continue;
829
830 bool bCreateErrorBar = false;
831 {
832 uno::Reference< beans::XPropertySet > xErrorBarProp(pSeries->getYErrorBarProperties(nIndex));
833 if( xErrorBarProp.is() )
834 {
835 bool bShowPositive = false;
836 bool bShowNegative = false;
837 xErrorBarProp->getPropertyValue( C2U( "ShowPositiveError" )) >>= bShowPositive;
838 xErrorBarProp->getPropertyValue( C2U( "ShowNegativeError" )) >>= bShowNegative;
839 bCreateErrorBar = bShowPositive || bShowNegative;
840 }
841 }
842
843 Symbol* pSymbolProperties = m_bSymbol ? (*aSeriesIter)->getSymbolProperties( nIndex ) : 0;
844 bool bCreateSymbol = pSymbolProperties && (pSymbolProperties->Style != SymbolStyle_NONE);
845
846 if( !bCreateSymbol && !bCreateErrorBar && !pSeries->getDataPointLabelIfLabel(nIndex) )
847 continue;
848
849 //create a group shape for this point and add to the series shape:
850 rtl::OUString aPointCID = ObjectIdentifier::createPointCID(
851 (*aSeriesIter)->getPointCID_Stub(), nIndex );
852 uno::Reference< drawing::XShapes > xPointGroupShape_Shapes(
853 createGroupShape(xSeriesGroupShape_Shapes,aPointCID) );
854 uno::Reference<drawing::XShape> xPointGroupShape_Shape =
855 uno::Reference<drawing::XShape>( xPointGroupShape_Shapes, uno::UNO_QUERY );
856
857 {
858 nCreatedPoints++;
859
860 //create data point
861 drawing::Direction3D aSymbolSize(0,0,0);
862 if( bCreateSymbol )
863 {
864 if(m_nDimension!=3)
865 {
866 if( pSymbolProperties )
867 {
868 if( pSymbolProperties->Style != SymbolStyle_NONE )
869 {
870 aSymbolSize.DirectionX = pSymbolProperties->Size.Width;
871 aSymbolSize.DirectionY = pSymbolProperties->Size.Height;
872 }
873
874 if( pSymbolProperties->Style == SymbolStyle_STANDARD )
875 {
876 sal_Int32 nSymbol = pSymbolProperties->StandardSymbol;
877 m_pShapeFactory->createSymbol2D( xPointGroupShape_Shapes
878 , aScenePosition, aSymbolSize
879 , nSymbol
880 , pSymbolProperties->BorderColor
881 , pSymbolProperties->FillColor );
882 }
883 else if( pSymbolProperties->Style == SymbolStyle_GRAPHIC )
884 {
885 m_pShapeFactory->createGraphic2D( xPointGroupShape_Shapes
886 , aScenePosition , aSymbolSize
887 , pSymbolProperties->Graphic );
888 }
889 //@todo other symbol styles
890 }
891 }
892 }
893 //create error bar
894 createErrorBar_Y( aUnscaledLogicPosition, **aSeriesIter, nIndex, m_xErrorBarTarget );
895
896 //create data point label
897 if( (**aSeriesIter).getDataPointLabelIfLabel(nIndex) )
898 {
899 LabelAlignment eAlignment = LABEL_ALIGN_TOP;
900 drawing::Position3D aScenePosition3D( aScenePosition.PositionX
901 , aScenePosition.PositionY
902 , aScenePosition.PositionZ+this->getTransformedDepth() );
903
904 sal_Int32 nLabelPlacement = pSeries->getLabelPlacement( nIndex, m_xChartTypeModel, m_nDimension, pPosHelper->isSwapXAndY() );
905
906 switch(nLabelPlacement)
907 {
908 case ::com::sun::star::chart::DataLabelPlacement::TOP:
909 aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1);
910 eAlignment = LABEL_ALIGN_TOP;
911 break;
912 case ::com::sun::star::chart::DataLabelPlacement::BOTTOM:
913 aScenePosition3D.PositionY += (aSymbolSize.DirectionY/2+1);
914 eAlignment = LABEL_ALIGN_BOTTOM;
915 break;
916 case ::com::sun::star::chart::DataLabelPlacement::LEFT:
917 aScenePosition3D.PositionX -= (aSymbolSize.DirectionX/2+1);
918 eAlignment = LABEL_ALIGN_LEFT;
919 break;
920 case ::com::sun::star::chart::DataLabelPlacement::RIGHT:
921 aScenePosition3D.PositionX += (aSymbolSize.DirectionX/2+1);
922 eAlignment = LABEL_ALIGN_RIGHT;
923 break;
924 case ::com::sun::star::chart::DataLabelPlacement::CENTER:
925 eAlignment = LABEL_ALIGN_CENTER;
926 //todo implement this different for area charts
927 break;
928 default:
929 DBG_ERROR("this label alignment is not implemented yet");
930 aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1);
931 eAlignment = LABEL_ALIGN_TOP;
932 break;
933 }
934
935 awt::Point aScreenPosition2D;//get the screen position for the labels
936 sal_Int32 nOffset = 100; //todo maybe calculate this font height dependent
937 if( m_bIsPolarCooSys && nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::OUTSIDE )
938 {
939 PolarPlottingPositionHelper* pPolarPosHelper = dynamic_cast<PolarPlottingPositionHelper*>(pPosHelper);
940 if( pPolarPosHelper )
941 {
942 PolarLabelPositionHelper aPolarLabelPositionHelper(pPolarPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory);
943 aScreenPosition2D = awt::Point( aPolarLabelPositionHelper.getLabelScreenPositionAndAlignmentForLogicValues(
944 eAlignment, fLogicX, fLogicY, fLogicZ, nOffset ));
945 }
946 }
947 else
948 {
949 if(LABEL_ALIGN_CENTER==eAlignment || m_nDimension == 3 )
950 nOffset = 0;
951 aScreenPosition2D = awt::Point( LabelPositionHelper(pPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory)
952 .transformSceneToScreenPosition( aScenePosition3D ) );
953 }
954
955 this->createDataLabel( m_xTextTarget, **aSeriesIter, nIndex
956 , fLogicValueForLabeDisplay
957 , aLogicYSumMap[nAttachedAxisIndex], aScreenPosition2D, eAlignment, nOffset );
958 }
959 }
960
961 //remove PointGroupShape if empty
962 if(!xPointGroupShape_Shapes->getCount())
963 xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shape);
964
965 }//next series in x slot (next y slot)
966 }//next x slot
967 }//next z slot
968 }//next category
969 //=============================================================================
970 //=============================================================================
971 //=============================================================================
972
973 impl_createSeriesShapes();
974
975 /* @todo remove series shapes if empty
976 //remove and delete point-group-shape if empty
977 if(!xSeriesGroupShape_Shapes->getCount())
978 {
979 (*aSeriesIter)->m_xShape.set(NULL);
980 m_xLogicTarget->remove(xSeriesGroupShape_Shape);
981 }
982 */
983
984 //remove and delete series-group-shape if empty
985
986 //... todo
987
988 OSL_TRACE( "\nPPPPPPPPP<<<<<<<<<<<< area chart :: createShapes():: skipped points: %d created points: %d", nSkippedPoints, nCreatedPoints );
989 }
990
991 //.............................................................................
992 } //namespace chart
993 //.............................................................................
994