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 "VSeriesPlotter.hxx"
29 #include "ShapeFactory.hxx"
30 #include "chartview/ExplicitValueProvider.hxx"
31 
32 #include "CommonConverters.hxx"
33 #include "macros.hxx"
34 #include "ViewDefines.hxx"
35 #include "ObjectIdentifier.hxx"
36 #include "StatisticsHelper.hxx"
37 #include "PlottingPositionHelper.hxx"
38 #include "LabelPositionHelper.hxx"
39 #include "ChartTypeHelper.hxx"
40 #include "Clipping.hxx"
41 #include "servicenames_charttypes.hxx"
42 #include "NumberFormatterWrapper.hxx"
43 #include "ContainerHelper.hxx"
44 #include "DataSeriesHelper.hxx"
45 #include "RegressionCurveHelper.hxx"
46 #include "VLegendSymbolFactory.hxx"
47 #include "FormattedStringHelper.hxx"
48 #include "ResId.hxx"
49 #include "Strings.hrc"
50 #include "RelativePositionHelper.hxx"
51 #include "DateHelper.hxx"
52 #include "DiagramHelper.hxx"
53 
54 //only for creation: @todo remove if all plotter are uno components and instanciated via servicefactory
55 #include "BarChart.hxx"
56 #include "PieChart.hxx"
57 #include "AreaChart.hxx"
58 #include "CandleStickChart.hxx"
59 #include "BubbleChart.hxx"
60 //
61 
62 #include <com/sun/star/chart/ErrorBarStyle.hpp>
63 #include <com/sun/star/chart/TimeUnit.hpp>
64 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
65 #include <com/sun/star/container/XChild.hpp>
66 #include <com/sun/star/chart2/RelativePosition.hpp>
67 #include <editeng/unoprnms.hxx>
68 #include <tools/color.hxx>
69 // header for class OUStringBuffer
70 #include <rtl/ustrbuf.hxx>
71 #include <rtl/math.hxx>
72 #include <tools/debug.hxx>
73 #include <basegfx/vector/b2dvector.hxx>
74 #include <com/sun/star/drawing/LineStyle.hpp>
75 #include <com/sun/star/util/XCloneable.hpp>
76 
77 #include <svx/unoshape.hxx>
78 
79 #include <functional>
80 
81 //.............................................................................
82 namespace chart
83 {
84 //.............................................................................
85 using namespace ::com::sun::star;
86 using namespace ::com::sun::star::chart2;
87 using ::com::sun::star::uno::Reference;
88 using ::com::sun::star::uno::Sequence;
89 using rtl::OUString;
90 
91 //-----------------------------------------------------------------------------
92 //-----------------------------------------------------------------------------
93 //-----------------------------------------------------------------------------
94 
CachedYValues()95 VDataSeriesGroup::CachedYValues::CachedYValues()
96         : m_bValuesDirty(true)
97         , m_fMinimumY(0.0)
98         , m_fMaximumY(0.0)
99 {
100 }
101 
VDataSeriesGroup()102 VDataSeriesGroup::VDataSeriesGroup()
103         : m_aSeriesVector()
104         , m_bMaxPointCountDirty(true)
105         , m_nMaxPointCount(0)
106         , m_aListOfCachedYValues()
107 {
108 }
109 
VDataSeriesGroup(VDataSeries * pSeries)110 VDataSeriesGroup::VDataSeriesGroup( VDataSeries* pSeries )
111         : m_aSeriesVector(1,pSeries)
112         , m_bMaxPointCountDirty(true)
113         , m_nMaxPointCount(0)
114         , m_aListOfCachedYValues()
115 {
116 }
117 
~VDataSeriesGroup()118 VDataSeriesGroup::~VDataSeriesGroup()
119 {
120 }
121 
deleteSeries()122 void VDataSeriesGroup::deleteSeries()
123 {
124     //delete all data series help objects:
125     ::std::vector< VDataSeries* >::const_iterator       aIter = m_aSeriesVector.begin();
126     const ::std::vector< VDataSeries* >::const_iterator aEnd  = m_aSeriesVector.end();
127 	for( ; aIter != aEnd; aIter++ )
128     {
129         delete *aIter;
130     }
131     m_aSeriesVector.clear();
132 }
133 
addSeries(VDataSeries * pSeries)134 void VDataSeriesGroup::addSeries( VDataSeries* pSeries )
135 {
136     m_aSeriesVector.push_back(pSeries);
137     m_bMaxPointCountDirty=true;
138 }
139 
getSeriesCount() const140 sal_Int32 VDataSeriesGroup::getSeriesCount() const
141 {
142     return m_aSeriesVector.size();
143 }
144 
145 //-----------------------------------------------------------------------------
146 //-----------------------------------------------------------------------------
147 //-----------------------------------------------------------------------------
148 
VSeriesPlotter(const uno::Reference<XChartType> & xChartTypeModel,sal_Int32 nDimensionCount,bool bCategoryXAxis)149 VSeriesPlotter::VSeriesPlotter( const uno::Reference<XChartType>& xChartTypeModel
150                                , sal_Int32 nDimensionCount, bool bCategoryXAxis )
151         : PlotterBase( nDimensionCount )
152         , m_pMainPosHelper( 0 )
153         , m_xChartTypeModel(xChartTypeModel)
154         , m_xChartTypeModelProps( uno::Reference< beans::XPropertySet >::query( xChartTypeModel ))
155         , m_aZSlots()
156         , m_bCategoryXAxis(bCategoryXAxis)
157         , m_nTimeResolution(::com::sun::star::chart::TimeUnit::DAY)
158         , m_aNullDate(30,12,1899)
159         , m_xColorScheme()
160         , m_pExplicitCategoriesProvider(0)
161         , m_bPointsWereSkipped(false)
162 {
163     DBG_ASSERT(m_xChartTypeModel.is(),"no XChartType available in view, fallback to default values may be wrong");
164 }
165 
~VSeriesPlotter()166 VSeriesPlotter::~VSeriesPlotter()
167 {
168     //delete all data series help objects:
169     ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator             aZSlotIter = m_aZSlots.begin();
170     const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator  aZSlotEnd = m_aZSlots.end();
171     for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ )
172     {
173         ::std::vector< VDataSeriesGroup >::iterator             aXSlotIter = aZSlotIter->begin();
174         const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
175         for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ )
176         {
177             aXSlotIter->deleteSeries();
178         }
179         aZSlotIter->clear();
180     }
181     m_aZSlots.clear();
182 
183     tSecondaryPosHelperMap::iterator aPosIt = m_aSecondaryPosHelperMap.begin();
184     while( aPosIt != m_aSecondaryPosHelperMap.end() )
185     {
186         PlottingPositionHelper* pPosHelper = aPosIt->second;
187         if( pPosHelper )
188             delete pPosHelper;
189         ++aPosIt;
190     }
191     m_aSecondaryPosHelperMap.clear();
192 
193     m_aSecondaryValueScales.clear();
194 }
195 
addSeries(VDataSeries * pSeries,sal_Int32 zSlot,sal_Int32 xSlot,sal_Int32 ySlot)196 void VSeriesPlotter::addSeries( VDataSeries* pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot )
197 {
198     //take ownership of pSeries
199 
200     DBG_ASSERT( pSeries, "series to add is NULL" );
201     if(!pSeries)
202         return;
203 
204     if(m_bCategoryXAxis)
205     {
206         if( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() )
207             pSeries->setXValues( m_pExplicitCategoriesProvider->getOriginalCategories() );
208         else
209             pSeries->setCategoryXAxis();
210     }
211     else
212     {
213         if( m_pExplicitCategoriesProvider )
214             pSeries->setXValuesIfNone( m_pExplicitCategoriesProvider->getOriginalCategories() );
215     }
216 
217     if(zSlot<0 || zSlot>=static_cast<sal_Int32>(m_aZSlots.size()))
218     {
219         //new z slot
220         ::std::vector< VDataSeriesGroup > aZSlot;
221         aZSlot.push_back( VDataSeriesGroup(pSeries) );
222         m_aZSlots.push_back( aZSlot );
223     }
224     else
225     {
226         //existing zslot
227         ::std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[zSlot];
228 
229         if(xSlot<0 || xSlot>=static_cast<sal_Int32>(rXSlots.size()))
230         {
231             //append the series to already existing x series
232             rXSlots.push_back( VDataSeriesGroup(pSeries) );
233         }
234         else
235         {
236             //x slot is already occupied
237             //y slot decides what to do:
238 
239             VDataSeriesGroup& rYSlots = rXSlots[xSlot];
240             sal_Int32 nYSlotCount = rYSlots.getSeriesCount();
241 
242             if( ySlot < -1 )
243             {
244                 //move all existing series in the xSlot to next slot
245                 //@todo
246                 OSL_ENSURE( false, "Not implemented yet");
247             }
248             else if( ySlot == -1 || ySlot >= nYSlotCount)
249             {
250                 //append the series to already existing y series
251                 rYSlots.addSeries(pSeries);
252             }
253             else
254             {
255                 //y slot is already occupied
256                 //insert at given y and x position
257 
258                 //@todo
259                 OSL_ENSURE( false, "Not implemented yet");
260             }
261         }
262     }
263 }
264 
getPreferredDiagramAspectRatio() const265 drawing::Direction3D VSeriesPlotter::getPreferredDiagramAspectRatio() const
266 {
267     drawing::Direction3D aRet(1.0,1.0,1.0);
268     drawing::Direction3D aScale( m_pPosHelper->getScaledLogicWidth() );
269     aRet.DirectionZ = aScale.DirectionZ*0.2;
270     if(aRet.DirectionZ>1.0)
271         aRet.DirectionZ=1.0;
272     if(aRet.DirectionZ>10)
273         aRet.DirectionZ=10;
274     return aRet;
275 }
276 
keepAspectRatio() const277 bool VSeriesPlotter::keepAspectRatio() const
278 {
279     return true;
280 }
281 
releaseShapes()282 void VSeriesPlotter::releaseShapes()
283 {
284     ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator             aZSlotIter = m_aZSlots.begin();
285     const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
286     for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ )
287     {
288         ::std::vector< VDataSeriesGroup >::iterator             aXSlotIter = aZSlotIter->begin();
289         const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
290         for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ )
291         {
292             ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
293 
294             ::std::vector< VDataSeries* >::iterator             aSeriesIter = pSeriesList->begin();
295             const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd  = pSeriesList->end();
296 
297             //iterate through all series in this x slot
298 	        for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ )
299             {
300                 VDataSeries* pSeries( *aSeriesIter );
301                 pSeries->releaseShapes();
302             }
303         }
304     }
305 }
306 
getSeriesGroupShape(VDataSeries * pDataSeries,const uno::Reference<drawing::XShapes> & xTarget)307 uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShape( VDataSeries* pDataSeries
308                                         , const uno::Reference< drawing::XShapes >& xTarget )
309 {
310     uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xGroupShape );
311     if( !xShapes.is() )
312     {
313         //create a group shape for this series and add to logic target:
314         xShapes = createGroupShape( xTarget,pDataSeries->getCID() );
315         pDataSeries->m_xGroupShape = xShapes;
316     }
317     return xShapes;
318 }
319 
getSeriesGroupShapeFrontChild(VDataSeries * pDataSeries,const uno::Reference<drawing::XShapes> & xTarget)320 uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShapeFrontChild( VDataSeries* pDataSeries
321                                         , const uno::Reference< drawing::XShapes >& xTarget )
322 {
323     uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xFrontSubGroupShape );
324     if(!xShapes.is())
325     {
326         //ensure that the series group shape is already created
327         uno::Reference< drawing::XShapes > xSeriesShapes( this->getSeriesGroupShape( pDataSeries, xTarget ) );
328         //ensure that the back child is created first
329         this->getSeriesGroupShapeBackChild( pDataSeries, xTarget );
330         //use series group shape as parent for the new created front group shape
331         xShapes = createGroupShape( xSeriesShapes );
332         pDataSeries->m_xFrontSubGroupShape = xShapes;
333     }
334     return xShapes;
335 }
336 
getSeriesGroupShapeBackChild(VDataSeries * pDataSeries,const uno::Reference<drawing::XShapes> & xTarget)337 uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShapeBackChild( VDataSeries* pDataSeries
338                                         , const uno::Reference< drawing::XShapes >& xTarget )
339 {
340     uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xBackSubGroupShape );
341     if(!xShapes.is())
342     {
343         //ensure that the series group shape is already created
344         uno::Reference< drawing::XShapes > xSeriesShapes( this->getSeriesGroupShape( pDataSeries, xTarget ) );
345         //use series group shape as parent for the new created back group shape
346         xShapes = createGroupShape( xSeriesShapes );
347         pDataSeries->m_xBackSubGroupShape = xShapes;
348     }
349     return xShapes;
350 }
351 
getLabelsGroupShape(VDataSeries & rDataSeries,const uno::Reference<drawing::XShapes> & xTextTarget)352 uno::Reference< drawing::XShapes > VSeriesPlotter::getLabelsGroupShape( VDataSeries& rDataSeries
353                                         , const uno::Reference< drawing::XShapes >& xTextTarget )
354 {
355     //xTextTarget needs to be a 2D shape container always!
356 
357     uno::Reference< drawing::XShapes > xShapes( rDataSeries.m_xLabelsGroupShape );
358     if(!xShapes.is())
359     {
360         //create a 2D group shape for texts of this series and add to text target:
361         xShapes = m_pShapeFactory->createGroup2D( xTextTarget, rDataSeries.getLabelsCID() );
362         rDataSeries.m_xLabelsGroupShape = xShapes;
363     }
364     return xShapes;
365 }
366 
getErrorBarsGroupShape(VDataSeries & rDataSeries,const uno::Reference<drawing::XShapes> & xTarget)367 uno::Reference< drawing::XShapes > VSeriesPlotter::getErrorBarsGroupShape( VDataSeries& rDataSeries
368                                         , const uno::Reference< drawing::XShapes >& xTarget )
369 {
370     uno::Reference< drawing::XShapes > xShapes( rDataSeries.m_xErrorBarsGroupShape );
371     if(!xShapes.is())
372     {
373         //create a group shape for this series and add to logic target:
374         xShapes = this->createGroupShape( xTarget,rDataSeries.getErrorBarsCID() );
375         rDataSeries.m_xErrorBarsGroupShape = xShapes;
376     }
377     return xShapes;
378 
379 }
380 
getLabelTextForValue(VDataSeries & rDataSeries,sal_Int32 nPointIndex,double fValue,bool bAsPercentage)381 OUString VSeriesPlotter::getLabelTextForValue( VDataSeries& rDataSeries
382                 , sal_Int32 nPointIndex
383                 , double fValue
384                 , bool bAsPercentage )
385 {
386     OUString aNumber;
387 
388     if( m_apNumberFormatterWrapper.get())
389     {
390         sal_Int32 nNumberFormatKey = 0;
391         if( rDataSeries.hasExplicitNumberFormat(nPointIndex,bAsPercentage) )
392             nNumberFormatKey = rDataSeries.getExplicitNumberFormat(nPointIndex,bAsPercentage);
393         else if( bAsPercentage )
394         {
395             sal_Int32 nPercentFormat = DiagramHelper::getPercentNumberFormat( m_apNumberFormatterWrapper->getNumberFormatsSupplier() );
396             if( nPercentFormat != -1 )
397                 nNumberFormatKey = nPercentFormat;
398         }
399         else
400         {
401             if( rDataSeries.shouldLabelNumberFormatKeyBeDetectedFromYAxis() && m_aAxesNumberFormats.hasFormat(1,rDataSeries.getAttachedAxisIndex()) ) //y-axis
402                 nNumberFormatKey = m_aAxesNumberFormats.getFormat(1,rDataSeries.getAttachedAxisIndex());
403             else
404                 nNumberFormatKey = rDataSeries.detectNumberFormatKey( nPointIndex );
405         }
406         if(nNumberFormatKey<0)
407             nNumberFormatKey=0;
408 
409         sal_Int32 nLabelCol = 0;
410         bool bColChanged;
411         aNumber = m_apNumberFormatterWrapper->getFormattedString(
412                 nNumberFormatKey, fValue, nLabelCol, bColChanged );
413         //@todo: change color of label if bColChanged is true
414     }
415     else
416     {
417         sal_Unicode cDecSeparator = '.';//@todo get this locale dependent
418         aNumber = ::rtl::math::doubleToUString( fValue, rtl_math_StringFormat_G /*rtl_math_StringFormat*/
419             , 3/*DecPlaces*/ , cDecSeparator /*,sal_Int32 const * pGroups*/ /*,sal_Unicode cGroupSeparator*/ ,false /*bEraseTrailingDecZeros*/ );
420     }
421     return aNumber;
422 }
423 
createDataLabel(const uno::Reference<drawing::XShapes> & xTarget,VDataSeries & rDataSeries,sal_Int32 nPointIndex,double fValue,double fSumValue,const awt::Point & rScreenPosition2D,LabelAlignment eAlignment,sal_Int32 nOffset)424 uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Reference< drawing::XShapes >& xTarget
425                     , VDataSeries& rDataSeries
426                     , sal_Int32 nPointIndex
427                     , double fValue
428                     , double fSumValue
429                     , const awt::Point& rScreenPosition2D
430                     , LabelAlignment eAlignment
431                     , sal_Int32 nOffset )
432 {
433     uno::Reference< drawing::XShape > xTextShape;
434 
435     try
436     {
437         awt::Point aScreenPosition2D(rScreenPosition2D);
438         if(LABEL_ALIGN_LEFT==eAlignment)
439             aScreenPosition2D.X -= nOffset;
440         else if(LABEL_ALIGN_RIGHT==eAlignment)
441             aScreenPosition2D.X += nOffset;
442         else if(LABEL_ALIGN_TOP==eAlignment)
443             aScreenPosition2D.Y -= nOffset;
444         else if(LABEL_ALIGN_BOTTOM==eAlignment)
445             aScreenPosition2D.Y += nOffset;
446 
447         uno::Reference< drawing::XShapes > xTarget_(
448                 m_pShapeFactory->createGroup2D( this->getLabelsGroupShape(rDataSeries, xTarget)
449                     , ObjectIdentifier::createPointCID( rDataSeries.getLabelCID_Stub(),nPointIndex ) ) );
450 
451         //check whether the label needs to be created and how:
452         DataPointLabel* pLabel = rDataSeries.getDataPointLabelIfLabel( nPointIndex );
453 
454         if( !pLabel )
455             return xTextShape;
456 
457         //------------------------------------------------
458         //prepare legend symbol
459 
460         float fViewFontSize( 10.0 );
461         {
462             uno::Reference< beans::XPropertySet > xProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) );
463             if( xProps.is() )
464                 xProps->getPropertyValue( C2U( "CharHeight" )) >>= fViewFontSize;
465             // pt -> 1/100th mm
466             fViewFontSize *= (2540.0 / 72.0);
467         }
468         Reference< drawing::XShape > xSymbol;
469         if(pLabel->ShowLegendSymbol)
470         {
471             sal_Int32 nSymbolHeigth = static_cast< sal_Int32 >( fViewFontSize * 0.6  );
472             awt::Size aCurrentRatio = this->getPreferredLegendKeyAspectRatio();
473             sal_Int32 nSymbolWidth = aCurrentRatio.Width;
474             if( aCurrentRatio.Height > 0 )
475             {
476                 nSymbolWidth = nSymbolHeigth* aCurrentRatio.Width/aCurrentRatio.Height;
477             }
478             awt::Size aMaxSymbolExtent( nSymbolWidth, nSymbolHeigth );
479 
480             if( rDataSeries.isVaryColorsByPoint() )
481                 xSymbol.set( VSeriesPlotter::createLegendSymbolForPoint( aMaxSymbolExtent, rDataSeries, nPointIndex, xTarget_, m_xShapeFactory ) );
482             else
483                 xSymbol.set( VSeriesPlotter::createLegendSymbolForSeries( aMaxSymbolExtent, rDataSeries, xTarget_, m_xShapeFactory ) );
484 
485         }
486         //prepare text
487         ::rtl::OUStringBuffer aText;
488         ::rtl::OUString aSeparator(sal_Unicode(' '));
489         double fRotationDegrees = 0.0;
490         try
491         {
492             uno::Reference< beans::XPropertySet > xPointProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) );
493             if(xPointProps.is())
494             {
495                 xPointProps->getPropertyValue( C2U( "LabelSeparator" ) ) >>= aSeparator;
496                 xPointProps->getPropertyValue( C2U( "TextRotation" ) ) >>= fRotationDegrees;
497             }
498         }
499         catch( uno::Exception& e )
500         {
501             ASSERT_EXCEPTION( e );
502         }
503         bool bMultiLineLabel = aSeparator.equals(C2U("\n"));;
504         sal_Int32 nLineCountForSymbolsize = 0;
505         {
506             if(pLabel->ShowCategoryName)
507             {
508                 if( m_pExplicitCategoriesProvider )
509                 {
510                     Sequence< OUString > aCategories( m_pExplicitCategoriesProvider->getSimpleCategories() );
511                     if( nPointIndex >= 0 && nPointIndex < aCategories.getLength() )
512                     {
513                         aText.append( aCategories[nPointIndex] );
514                         ++nLineCountForSymbolsize;
515                     }
516                 }
517             }
518 
519             if(pLabel->ShowNumber)
520             {
521                 OUString aNumber( this->getLabelTextForValue( rDataSeries
522                     , nPointIndex, fValue, false /*bAsPercentage*/ ) );
523                 if( !aNumber.isEmpty() )
524                 {
525                     if(aText.getLength())
526                         aText.append(aSeparator);
527                     aText.append(aNumber);
528                     ++nLineCountForSymbolsize;
529                 }
530             }
531 
532             if(pLabel->ShowNumberInPercent)
533             {
534                 if(fSumValue==0.0)
535                     fSumValue=1.0;
536                 fValue /= fSumValue;
537                 if( fValue < 0 )
538                     fValue*=-1.0;
539 
540                 OUString aPercentage( this->getLabelTextForValue( rDataSeries
541                     , nPointIndex, fValue, true /*bAsPercentage*/ ) );
542                 if( !aPercentage.isEmpty() )
543                 {
544                     if(aText.getLength())
545                         aText.append(aSeparator);
546                     aText.append(aPercentage);
547                     ++nLineCountForSymbolsize;
548                 }
549             }
550         }
551         //------------------------------------------------
552         //prepare properties for multipropertyset-interface of shape
553         tNameSequence* pPropNames;
554         tAnySequence* pPropValues;
555         if( !rDataSeries.getTextLabelMultiPropertyLists( nPointIndex, pPropNames, pPropValues ) )
556             return xTextShape;
557         LabelPositionHelper::changeTextAdjustment( *pPropValues, *pPropNames, eAlignment );
558 
559         //------------------------------------------------
560         //create text shape
561         xTextShape = ShapeFactory(m_xShapeFactory).
562             createText( xTarget_, aText.makeStringAndClear()
563                         , *pPropNames, *pPropValues, ShapeFactory::makeTransformation( aScreenPosition2D ) );
564 
565         if( !xTextShape.is() )
566             return xTextShape;
567 
568         const awt::Point aUnrotatedTextPos( xTextShape->getPosition() );
569         if( fRotationDegrees != 0.0 )
570         {
571             const double fDegreesPi( fRotationDegrees * ( F_PI / -180.0 ) );
572             uno::Reference< beans::XPropertySet > xProp( xTextShape, uno::UNO_QUERY );
573             if( xProp.is() )
574                 xProp->setPropertyValue( C2U( "Transformation" ), ShapeFactory::makeTransformation( aScreenPosition2D, fDegreesPi ) );
575             LabelPositionHelper::correctPositionForRotation( xTextShape, eAlignment, fRotationDegrees, true /*bRotateAroundCenter*/ );
576         }
577 
578         if( xSymbol.is() )
579         {
580             const awt::Point aOldTextPos( xTextShape->getPosition() );
581             awt::Point aNewTextPos( aOldTextPos );
582 
583             awt::Point aSymbolPosition( aUnrotatedTextPos );
584             awt::Size aSymbolSize( xSymbol->getSize() );
585             awt::Size aTextSize( xTextShape->getSize() );
586 
587             sal_Int32 nXDiff = aSymbolSize.Width + static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.22 ) );//minimum 1mm
588             if( !bMultiLineLabel || nLineCountForSymbolsize <= 0 )
589                 nLineCountForSymbolsize = 1;
590             aSymbolPosition.Y += ((aTextSize.Height/nLineCountForSymbolsize)/4);
591 
592             if(LABEL_ALIGN_LEFT==eAlignment
593                 || LABEL_ALIGN_LEFT_TOP==eAlignment
594                 || LABEL_ALIGN_LEFT_BOTTOM==eAlignment)
595             {
596                 aSymbolPosition.X -= nXDiff;
597             }
598             else if(LABEL_ALIGN_RIGHT==eAlignment
599                 || LABEL_ALIGN_RIGHT_TOP==eAlignment
600                 || LABEL_ALIGN_RIGHT_BOTTOM==eAlignment )
601             {
602                 aNewTextPos.X += nXDiff;
603             }
604             else if(LABEL_ALIGN_TOP==eAlignment
605                 || LABEL_ALIGN_BOTTOM==eAlignment
606                 || LABEL_ALIGN_CENTER==eAlignment )
607             {
608                 aSymbolPosition.X -= nXDiff/2;
609                 aNewTextPos.X += nXDiff/2;
610             }
611 
612             xSymbol->setPosition( aSymbolPosition );
613             xTextShape->setPosition( aNewTextPos );
614         }
615     }
616     catch( uno::Exception& e )
617     {
618         ASSERT_EXCEPTION( e );
619     }
620 
621     return xTextShape;
622 }
623 
624 namespace
625 {
lcl_getErrorBarLogicLength(const uno::Sequence<double> & rData,uno::Reference<beans::XPropertySet> xProp,sal_Int32 nErrorBarStyle,sal_Int32 nIndex,bool bPositive)626 double lcl_getErrorBarLogicLength(
627     const uno::Sequence< double > & rData,
628     uno::Reference< beans::XPropertySet > xProp,
629     sal_Int32 nErrorBarStyle,
630     sal_Int32 nIndex,
631     bool bPositive )
632 {
633     double fResult;
634     ::rtl::math::setNan( & fResult );
635     try
636     {
637         switch( nErrorBarStyle )
638         {
639             case ::com::sun::star::chart::ErrorBarStyle::NONE:
640                 break;
641             case ::com::sun::star::chart::ErrorBarStyle::VARIANCE:
642                 fResult = StatisticsHelper::getVariance( rData );
643                 break;
644             case ::com::sun::star::chart::ErrorBarStyle::STANDARD_DEVIATION:
645                 fResult = StatisticsHelper::getStandardDeviation( rData );
646                 break;
647             case ::com::sun::star::chart::ErrorBarStyle::RELATIVE:
648             {
649                 double fPercent = 0;
650                 if( xProp->getPropertyValue( bPositive
651                                              ? C2U("PositiveError")
652                                              : C2U("NegativeError")) >>= fPercent )
653                 {
654                     if( nIndex >=0 && nIndex < rData.getLength() &&
655                         ! ::rtl::math::isNan( rData[nIndex] ) &&
656                         ! ::rtl::math::isNan( fPercent ))
657                     {
658                         fResult = rData[nIndex] * fPercent / 100.0;
659                     }
660                 }
661             }
662             break;
663             case ::com::sun::star::chart::ErrorBarStyle::ABSOLUTE:
664                 xProp->getPropertyValue( bPositive
665                                          ? C2U("PositiveError")
666                                          : C2U("NegativeError")) >>= fResult;
667                 break;
668             case ::com::sun::star::chart::ErrorBarStyle::ERROR_MARGIN:
669             {
670                 // todo: check if this is really what's called error-margin
671                 double fPercent = 0;
672                 if( xProp->getPropertyValue( bPositive
673                                              ? C2U("PositiveError")
674                                              : C2U("NegativeError")) >>= fPercent )
675                 {
676                     double fMaxValue;
677                     ::rtl::math::setInf(&fMaxValue, true);
678                     const double* pValues = rData.getConstArray();
679                     for(sal_Int32 i=0; i<rData.getLength(); ++i, ++pValues)
680                     {
681                         if(fMaxValue<*pValues)
682                             fMaxValue=*pValues;
683                     }
684                     if( ::rtl::math::isFinite( fMaxValue ) &&
685                         ::rtl::math::isFinite( fPercent ))
686                     {
687                         fResult = fMaxValue * fPercent / 100.0;
688                     }
689                 }
690             }
691             break;
692             case ::com::sun::star::chart::ErrorBarStyle::STANDARD_ERROR:
693                 fResult = StatisticsHelper::getStandardError( rData );
694                 break;
695             case ::com::sun::star::chart::ErrorBarStyle::FROM_DATA:
696             {
697                 uno::Reference< chart2::data::XDataSource > xErrorBarData( xProp, uno::UNO_QUERY );
698                 if( xErrorBarData.is())
699                     fResult = StatisticsHelper::getErrorFromDataSource(
700                         xErrorBarData, nIndex, bPositive /*, true */ /* y-error */ );
701             }
702             break;
703         }
704     }
705     catch( uno::Exception & e )
706     {
707         ASSERT_EXCEPTION( e );
708     }
709 
710     return fResult;
711 }
712 
lcl_AddErrorBottomLine(const drawing::Position3D & rPosition,::basegfx::B2DVector aMainDirection,drawing::PolyPolygonShape3D & rPoly,sal_Int32 nSequenceIndex)713 void lcl_AddErrorBottomLine( const drawing::Position3D& rPosition, ::basegfx::B2DVector aMainDirection
714                 , drawing::PolyPolygonShape3D& rPoly, sal_Int32 nSequenceIndex )
715 {
716     double fFixedWidth = 200.0;
717 
718     aMainDirection.normalize();
719     ::basegfx::B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX());
720     aOrthoDirection.normalize();
721 
722     ::basegfx::B2DVector aAnchor( rPosition.PositionX, rPosition.PositionY );
723     ::basegfx::B2DVector aStart = aAnchor + aOrthoDirection*fFixedWidth/2.0;
724     ::basegfx::B2DVector aEnd = aAnchor - aOrthoDirection*fFixedWidth/2.0;
725 
726     AddPointToPoly( rPoly, drawing::Position3D( aStart.getX(), aStart.getY(), rPosition.PositionZ), nSequenceIndex );
727     AddPointToPoly( rPoly, drawing::Position3D( aEnd.getX(), aEnd.getY(), rPosition.PositionZ), nSequenceIndex );
728 }
729 
lcl_getErrorBarMainDirection(const drawing::Position3D & rStart,const drawing::Position3D & rBottomEnd,PlottingPositionHelper * pPosHelper,const drawing::Position3D & rUnscaledLogicPosition,bool bYError)730 ::basegfx::B2DVector lcl_getErrorBarMainDirection(
731               const drawing::Position3D& rStart
732             , const drawing::Position3D& rBottomEnd
733             , PlottingPositionHelper* pPosHelper
734             , const drawing::Position3D& rUnscaledLogicPosition
735             , bool bYError )
736 {
737     ::basegfx::B2DVector aMainDirection = ::basegfx::B2DVector( rStart.PositionX - rBottomEnd.PositionX
738                                               , rStart.PositionY - rBottomEnd.PositionY );
739     if( !aMainDirection.getLength() )
740     {
741         //get logic clip values:
742         double MinX = pPosHelper->getLogicMinX();
743         double MinY = pPosHelper->getLogicMinY();
744         double MaxX = pPosHelper->getLogicMaxX();
745         double MaxY = pPosHelper->getLogicMaxY();
746         double fZ = pPosHelper->getLogicMinZ();
747 
748 
749         if( bYError )
750         {
751             //main direction has constant x value
752             MinX = rUnscaledLogicPosition.PositionX;
753             MaxX = rUnscaledLogicPosition.PositionX;
754         }
755         else
756         {
757             //main direction has constant y value
758             MinY = rUnscaledLogicPosition.PositionY;
759             MaxY = rUnscaledLogicPosition.PositionY;
760         }
761 
762         drawing::Position3D aStart = pPosHelper->transformLogicToScene( MinX, MinY, fZ, false );
763         drawing::Position3D aEnd = pPosHelper->transformLogicToScene( MaxX, MaxY, fZ, false );
764 
765         aMainDirection = ::basegfx::B2DVector( aStart.PositionX - aEnd.PositionX
766                                               , aStart.PositionY - aEnd.PositionY );
767     }
768     if( !aMainDirection.getLength() )
769     {
770         //@todo
771     }
772     return aMainDirection;
773 }
774 
lcl_transformMixedToScene(PlottingPositionHelper * pPosHelper,double fX,double fY,double fZ,bool bClip)775 drawing::Position3D lcl_transformMixedToScene( PlottingPositionHelper* pPosHelper
776     , double fX /*scaled*/, double fY /*unscaled*/, double fZ /*unscaled*/, bool bClip )
777 {
778     if(!pPosHelper)
779         return drawing::Position3D(0,0,0);
780     pPosHelper->doLogicScaling( 0,&fY,&fZ );
781     if(bClip)
782         pPosHelper->clipScaledLogicValues( &fX,&fY,&fZ );
783     return pPosHelper->transformScaledLogicToScene( fX, fY, fZ, false );
784 }
785 
786 } // anonymous namespace
787 
788 // virtual
createErrorBar(const uno::Reference<drawing::XShapes> & xTarget,const drawing::Position3D & rUnscaledLogicPosition,const uno::Reference<beans::XPropertySet> & xErrorBarProperties,const VDataSeries & rVDataSeries,sal_Int32 nIndex,bool bYError,double * pfScaledLogicX)789 void VSeriesPlotter::createErrorBar(
790       const uno::Reference< drawing::XShapes >& xTarget
791     , const drawing::Position3D& rUnscaledLogicPosition
792     , const uno::Reference< beans::XPropertySet > & xErrorBarProperties
793     , const VDataSeries& rVDataSeries
794     , sal_Int32 nIndex
795     , bool bYError /* = true */
796     , double* pfScaledLogicX
797     )
798 {
799     if( !ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel, m_nDimension ) )
800         return;
801 
802     if( ! xErrorBarProperties.is())
803         return;
804 
805     try
806     {
807         sal_Bool bShowPositive = sal_False;
808         sal_Bool bShowNegative = sal_False;
809         sal_Int32 nErrorBarStyle = ::com::sun::star::chart::ErrorBarStyle::VARIANCE;
810 
811         xErrorBarProperties->getPropertyValue( C2U( "ShowPositiveError" )) >>= bShowPositive;
812         xErrorBarProperties->getPropertyValue( C2U( "ShowNegativeError" )) >>= bShowNegative;
813         xErrorBarProperties->getPropertyValue( C2U( "ErrorBarStyle" )) >>= nErrorBarStyle;
814 
815         if(!bShowPositive && !bShowNegative)
816             return;
817 
818         if(nErrorBarStyle==::com::sun::star::chart::ErrorBarStyle::NONE)
819             return;
820 
821         drawing::Position3D aUnscaledLogicPosition(rUnscaledLogicPosition);
822         if(nErrorBarStyle==::com::sun::star::chart::ErrorBarStyle::STANDARD_DEVIATION)
823             aUnscaledLogicPosition.PositionY = rVDataSeries.getYMeanValue();
824 
825         bool bCreateNegativeBorder = false;//make a vertical line at the negative end of the error bar
826         bool bCreatePositiveBorder = false;//make a vertical line at the positive end of the error bar
827         drawing::Position3D aMiddle(aUnscaledLogicPosition);
828         const double fX = aUnscaledLogicPosition.PositionX;
829         const double fY = aUnscaledLogicPosition.PositionY;
830         const double fZ = aUnscaledLogicPosition.PositionZ;
831         double fScaledX = fX;
832         if( pfScaledLogicX )
833             fScaledX = *pfScaledLogicX;
834         else
835             m_pPosHelper->doLogicScaling( &fScaledX, 0, 0 );
836 
837         aMiddle = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fY, fZ, true );
838 
839         drawing::Position3D aNegative(aMiddle);
840         drawing::Position3D aPositive(aMiddle);
841 
842         uno::Sequence< double > aData( bYError ? rVDataSeries.getAllY() : rVDataSeries.getAllX() );
843 
844         if( bShowPositive )
845         {
846             double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, true );
847             if( ::rtl::math::isFinite( fLength ) )
848             {
849                 double fLocalX = fX;
850                 double fLocalY = fY;
851                 if( bYError )
852                 {
853                     fLocalY+=fLength;
854                     aPositive = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ, true );
855                 }
856                 else
857                 {
858                     fLocalX+=fLength;
859                     aPositive = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true );
860                 }
861                 bCreatePositiveBorder = m_pPosHelper->isLogicVisible(fLocalX, fLocalY, fZ);
862             }
863             else
864                 bShowPositive = false;
865         }
866 
867         if( bShowNegative )
868         {
869             double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, false );
870             if( ::rtl::math::isFinite( fLength ) )
871             {
872                 double fLocalX = fX;
873                 double fLocalY = fY;
874                 if( bYError )
875                 {
876                     fLocalY-=fLength;
877                     aNegative = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ, true );
878                 }
879                 else
880                 {
881                     fLocalX-=fLength;
882                     aNegative = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true );
883                 }
884                 bCreateNegativeBorder = m_pPosHelper->isLogicVisible( fLocalX, fLocalY, fZ);
885             }
886             else
887                 bShowNegative = false;
888         }
889 
890         if(!bShowPositive && !bShowNegative)
891             return;
892 
893         drawing::PolyPolygonShape3D aPoly;
894 
895         sal_Int32 nSequenceIndex=0;
896         if( bShowNegative )
897             AddPointToPoly( aPoly, aNegative, nSequenceIndex );
898         AddPointToPoly( aPoly, aMiddle, nSequenceIndex );
899         if( bShowPositive )
900             AddPointToPoly( aPoly, aPositive, nSequenceIndex );
901 
902         if( bShowNegative && bCreateNegativeBorder )
903         {
904             ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aNegative, m_pPosHelper, aUnscaledLogicPosition, bYError );
905             nSequenceIndex++;
906             lcl_AddErrorBottomLine( aNegative, aMainDirection, aPoly, nSequenceIndex );
907         }
908         if( bShowPositive && bCreatePositiveBorder )
909         {
910             ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aPositive, m_pPosHelper, aUnscaledLogicPosition, bYError );
911             nSequenceIndex++;
912             lcl_AddErrorBottomLine( aPositive, aMainDirection, aPoly, nSequenceIndex );
913         }
914 
915         uno::Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( xTarget, PolyToPointSequence( aPoly) );
916         this->setMappedProperties( xShape, xErrorBarProperties, PropertyMapper::getPropertyNameMapForLineProperties() );
917     }
918     catch( uno::Exception & e )
919     {
920         ASSERT_EXCEPTION( e );
921     }
922 
923 }
924 
925 // virtual
createErrorBar_Y(const drawing::Position3D & rUnscaledLogicPosition,VDataSeries & rVDataSeries,sal_Int32 nPointIndex,const uno::Reference<drawing::XShapes> & xTarget,double * pfScaledLogicX)926 void VSeriesPlotter::createErrorBar_Y( const drawing::Position3D& rUnscaledLogicPosition
927                             , VDataSeries& rVDataSeries, sal_Int32 nPointIndex
928                             , const uno::Reference< drawing::XShapes >& xTarget
929                             , double* pfScaledLogicX )
930 {
931     if(m_nDimension!=2)
932         return;
933     // error bars
934     uno::Reference< beans::XPropertySet > xErrorBarProp(rVDataSeries.getYErrorBarProperties(nPointIndex));
935     if( xErrorBarProp.is())
936     {
937         uno::Reference< drawing::XShapes > xErrorBarsGroup_Shapes(
938             this->getErrorBarsGroupShape(rVDataSeries, xTarget) );
939 
940         createErrorBar( xErrorBarsGroup_Shapes
941             , rUnscaledLogicPosition, xErrorBarProp
942             , rVDataSeries, nPointIndex
943             , true /* bYError */
944             , pfScaledLogicX );
945     }
946 }
947 
createRegressionCurvesShapes(VDataSeries & rVDataSeries,const uno::Reference<drawing::XShapes> & xTarget,const uno::Reference<drawing::XShapes> & xEquationTarget,bool bMaySkipPointsInRegressionCalculation)948 void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries& rVDataSeries
949                             , const uno::Reference< drawing::XShapes >& xTarget
950                             , const uno::Reference< drawing::XShapes >& xEquationTarget
951                             , bool bMaySkipPointsInRegressionCalculation )
952 {
953     if(m_nDimension!=2)
954         return;
955     uno::Reference< XRegressionCurveContainer > xRegressionContainer(
956                 rVDataSeries.getModel(), uno::UNO_QUERY );
957     if(!xRegressionContainer.is())
958         return;
959     double fMinX = m_pPosHelper->getLogicMinX();
960     double fMaxX = m_pPosHelper->getLogicMaxX();
961 
962     uno::Sequence< uno::Reference< XRegressionCurve > > aCurveList =
963         xRegressionContainer->getRegressionCurves();
964     for(sal_Int32 nN=0; nN<aCurveList.getLength(); nN++)
965     {
966         uno::Reference< XRegressionCurveCalculator > xRegressionCurveCalculator(
967             aCurveList[nN]->getCalculator() );
968         if( ! xRegressionCurveCalculator.is())
969             continue;
970         xRegressionCurveCalculator->recalculateRegression( rVDataSeries.getAllX(), rVDataSeries.getAllY() );
971 
972         sal_Int32 nRegressionPointCount = 50;//@todo find a more optimal solution if more complicated curve types are introduced
973         drawing::PolyPolygonShape3D aRegressionPoly;
974         aRegressionPoly.SequenceX.realloc(1);
975         aRegressionPoly.SequenceY.realloc(1);
976         aRegressionPoly.SequenceZ.realloc(1);
977         aRegressionPoly.SequenceX[0].realloc(nRegressionPointCount);
978         aRegressionPoly.SequenceY[0].realloc(nRegressionPointCount);
979         aRegressionPoly.SequenceZ[0].realloc(nRegressionPointCount);
980         sal_Int32 nRealPointCount=0;
981 
982         std::vector< ExplicitScaleData > aScales( m_pPosHelper->getScales());
983         uno::Reference< chart2::XScaling > xScalingX;
984         uno::Reference< chart2::XScaling > xScalingY;
985         if( aScales.size() >= 2 )
986         {
987             xScalingX.set( aScales[0].Scaling );
988             xScalingY.set( aScales[1].Scaling );
989         }
990 
991         uno::Sequence< geometry::RealPoint2D > aCalculatedPoints(
992             xRegressionCurveCalculator->getCurveValues(
993                 fMinX, fMaxX, nRegressionPointCount, xScalingX, xScalingY, bMaySkipPointsInRegressionCalculation ));
994         nRegressionPointCount = aCalculatedPoints.getLength();
995         for(sal_Int32 nP=0; nP<nRegressionPointCount; nP++)
996         {
997             double fLogicX = aCalculatedPoints[nP].X;
998             double fLogicY = aCalculatedPoints[nP].Y;
999             double fLogicZ = 0.0;//dummy
1000 
1001             m_pPosHelper->doLogicScaling( &fLogicX, &fLogicY, &fLogicZ );
1002 
1003             if(    !::rtl::math::isNan(fLogicX) && !::rtl::math::isInf(fLogicX)
1004                     && !::rtl::math::isNan(fLogicY) && !::rtl::math::isInf(fLogicY)
1005                     && !::rtl::math::isNan(fLogicZ) && !::rtl::math::isInf(fLogicZ) )
1006             {
1007                 aRegressionPoly.SequenceX[0][nRealPointCount] = fLogicX;
1008                 aRegressionPoly.SequenceY[0][nRealPointCount] = fLogicY;
1009                 nRealPointCount++;
1010             }
1011         }
1012         aRegressionPoly.SequenceX[0].realloc(nRealPointCount);
1013         aRegressionPoly.SequenceY[0].realloc(nRealPointCount);
1014         aRegressionPoly.SequenceZ[0].realloc(nRealPointCount);
1015 
1016         drawing::PolyPolygonShape3D aClippedPoly;
1017         Clipping::clipPolygonAtRectangle( aRegressionPoly, m_pPosHelper->getScaledLogicClipDoubleRect(), aClippedPoly );
1018         aRegressionPoly = aClippedPoly;
1019         m_pPosHelper->transformScaledLogicToScene( aRegressionPoly );
1020 
1021         awt::Point aDefaultPos;
1022         if( aRegressionPoly.SequenceX.getLength() && aRegressionPoly.SequenceX[0].getLength() )
1023         {
1024             uno::Reference< beans::XPropertySet > xCurveModelProp( aCurveList[nN], uno::UNO_QUERY );
1025             VLineProperties aVLineProperties;
1026             aVLineProperties.initFromPropertySet( xCurveModelProp );
1027 
1028             //create an extra group shape for each curve for selection handling
1029             bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurveList[nN] );
1030             uno::Reference< drawing::XShapes > xRegressionGroupShapes =
1031                 createGroupShape( xTarget, rVDataSeries.getDataCurveCID( nN, bAverageLine ) );
1032             uno::Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D(
1033                 xRegressionGroupShapes, PolyToPointSequence( aRegressionPoly ), &aVLineProperties );
1034             m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") );
1035             aDefaultPos = xShape->getPosition();
1036         }
1037 
1038         // curve equation and correlation coefficient
1039         uno::Reference< beans::XPropertySet > xEqProp( aCurveList[nN]->getEquationProperties());
1040         if( xEqProp.is())
1041         {
1042             createRegressionCurveEquationShapes(
1043                 rVDataSeries.getDataCurveEquationCID( nN ),
1044                 xEqProp, xEquationTarget, xRegressionCurveCalculator,
1045                 aDefaultPos );
1046         }
1047     }
1048 }
1049 
createRegressionCurveEquationShapes(const OUString & rEquationCID,const uno::Reference<beans::XPropertySet> & xEquationProperties,const uno::Reference<drawing::XShapes> & xEquationTarget,const uno::Reference<chart2::XRegressionCurveCalculator> & xRegressionCurveCalculator,awt::Point aDefaultPos)1050 void VSeriesPlotter::createRegressionCurveEquationShapes(
1051     const OUString & rEquationCID,
1052     const uno::Reference< beans::XPropertySet > & xEquationProperties,
1053     const uno::Reference< drawing::XShapes >& xEquationTarget,
1054     const uno::Reference< chart2::XRegressionCurveCalculator > & xRegressionCurveCalculator,
1055     awt::Point aDefaultPos )
1056 {
1057     OSL_ASSERT( xEquationProperties.is());
1058     if( !xEquationProperties.is())
1059         return;
1060 
1061     bool bShowEquation = false;
1062     bool bShowCorrCoeff = false;
1063     OUString aSep( sal_Unicode('\n'));
1064     if(( xEquationProperties->getPropertyValue( C2U("ShowEquation")) >>= bShowEquation ) &&
1065        ( xEquationProperties->getPropertyValue( C2U("ShowCorrelationCoefficient")) >>= bShowCorrCoeff ))
1066     {
1067         if( ! (bShowEquation || bShowCorrCoeff))
1068             return;
1069 
1070         ::rtl::OUStringBuffer aFormula;
1071         sal_Int32 nNumberFormatKey = 0;
1072         xEquationProperties->getPropertyValue( C2U("NumberFormat")) >>= nNumberFormatKey;
1073 
1074         if( bShowEquation )
1075         {
1076             if( m_apNumberFormatterWrapper.get())
1077             {
1078                 aFormula = xRegressionCurveCalculator->getFormattedRepresentation(
1079                     m_apNumberFormatterWrapper->getNumberFormatsSupplier(),
1080                     nNumberFormatKey );
1081             }
1082             else
1083             {
1084                 aFormula = xRegressionCurveCalculator->getRepresentation();
1085             }
1086 
1087             if( bShowCorrCoeff )
1088             {
1089 //                 xEquationProperties->getPropertyValue( C2U("Separator")) >>= aSep;
1090                 aFormula.append( aSep );
1091             }
1092         }
1093         if( bShowCorrCoeff )
1094         {
1095             aFormula.append( sal_Unicode( 'R' ));
1096             aFormula.append( sal_Unicode( 0x00b2 ));
1097             aFormula.append( C2U( " = " ));
1098             double fR( xRegressionCurveCalculator->getCorrelationCoefficient());
1099             if( m_apNumberFormatterWrapper.get())
1100             {
1101                 sal_Int32 nLabelCol = 0;
1102                 bool bColChanged;
1103                 aFormula.append(
1104                     m_apNumberFormatterWrapper->getFormattedString(
1105                         nNumberFormatKey, fR*fR, nLabelCol, bColChanged ));
1106                 //@todo: change color of label if bColChanged is true
1107             }
1108             else
1109             {
1110                 sal_Unicode aDecimalSep( '.' );//@todo get this locale dependent
1111                 aFormula.append( ::rtl::math::doubleToUString(
1112                                      fR*fR, rtl_math_StringFormat_G, 4, aDecimalSep, true ));
1113             }
1114         }
1115 
1116         awt::Point aScreenPosition2D;
1117         chart2::RelativePosition aRelativePosition;
1118         if( xEquationProperties->getPropertyValue( C2U("RelativePosition")) >>= aRelativePosition )
1119         {
1120             //@todo decide whether x is primary or secondary
1121             double fX = aRelativePosition.Primary*m_aPageReferenceSize.Width;
1122             double fY = aRelativePosition.Secondary*m_aPageReferenceSize.Height;
1123             aScreenPosition2D.X = static_cast< sal_Int32 >( ::rtl::math::round( fX ));
1124             aScreenPosition2D.Y = static_cast< sal_Int32 >( ::rtl::math::round( fY ));
1125         }
1126         else
1127             aScreenPosition2D = aDefaultPos;
1128 
1129         if( aFormula.getLength())
1130         {
1131             // set fill and line properties on creation
1132             tNameSequence aNames;
1133             tAnySequence  aValues;
1134             PropertyMapper::getPreparedTextShapePropertyLists( xEquationProperties, aNames, aValues );
1135 
1136             uno::Reference< drawing::XShape > xTextShape = m_pShapeFactory->createText(
1137                 xEquationTarget, aFormula.makeStringAndClear(),
1138                 aNames, aValues, ShapeFactory::makeTransformation( aScreenPosition2D ));
1139 
1140 //             // adapt font sizes
1141 //             awt::Size aOldRefSize;
1142 //             if( xTitleProperties->getPropertyValue( C2U("ReferencePageSize")) >>= aOldRefSize )
1143 //             {
1144 //                 uno::Reference< beans::XPropertySet > xShapeProp( xTextShape, uno::UNO_QUERY );
1145 //                 if( xShapeProp.is())
1146 //                     RelativeSizeHelper::adaptFontSizes( xShapeProp, aOldRefSize, m_aPageReferenceSize );
1147 //             }
1148 
1149             OSL_ASSERT( xTextShape.is());
1150             if( xTextShape.is())
1151             {
1152                 ShapeFactory::setShapeName( xTextShape, rEquationCID );
1153                 awt::Size aSize( xTextShape->getSize() );
1154                 awt::Point aPos( RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
1155                     aScreenPosition2D, aSize, aRelativePosition.Anchor ) );
1156                 //ensure that the equation is fully placed within the page (if possible)
1157                 if( (aPos.X + aSize.Width) > m_aPageReferenceSize.Width )
1158                     aPos.X = m_aPageReferenceSize.Width - aSize.Width;
1159                 if( aPos.X < 0 )
1160                     aPos.X = 0;
1161                 if( (aPos.Y + aSize.Height) > m_aPageReferenceSize.Height )
1162                     aPos.Y = m_aPageReferenceSize.Height - aSize.Height;
1163                 if( aPos.Y < 0 )
1164                     aPos.Y = 0;
1165                 xTextShape->setPosition(aPos);
1166             }
1167         }
1168     }
1169 }
1170 
1171 
setMappedProperties(const uno::Reference<drawing::XShape> & xTargetShape,const uno::Reference<beans::XPropertySet> & xSource,const tPropertyNameMap & rMap,tPropertyNameValueMap * pOverwriteMap)1172 void VSeriesPlotter::setMappedProperties(
1173           const uno::Reference< drawing::XShape >& xTargetShape
1174         , const uno::Reference< beans::XPropertySet >& xSource
1175         , const tPropertyNameMap& rMap
1176         , tPropertyNameValueMap* pOverwriteMap )
1177 {
1178     uno::Reference< beans::XPropertySet > xTargetProp( xTargetShape, uno::UNO_QUERY );
1179     PropertyMapper::setMappedProperties(xTargetProp,xSource,rMap,pOverwriteMap);
1180 }
1181 
setTimeResolutionOnXAxis(long TimeResolution,const Date & rNullDate)1182 void VSeriesPlotter::setTimeResolutionOnXAxis( long TimeResolution, const Date& rNullDate )
1183 {
1184     m_nTimeResolution = TimeResolution;
1185     m_aNullDate = rNullDate;
1186 }
1187 
1188 //-------------------------------------------------------------------------
1189 // MinimumAndMaximumSupplier
1190 //-------------------------------------------------------------------------
calculateTimeResolutionOnXAxis()1191 long VSeriesPlotter::calculateTimeResolutionOnXAxis()
1192 {
1193     long nRet = ::com::sun::star::chart::TimeUnit::YEAR;
1194     if( m_pExplicitCategoriesProvider )
1195     {
1196         const std::vector< DatePlusIndex >&  rDateCategories = m_pExplicitCategoriesProvider->getDateCategories();
1197         std::vector< DatePlusIndex >::const_iterator aIt = rDateCategories.begin(), aEnd = rDateCategories.end();
1198         Date aNullDate(30,12,1899);
1199         if( m_apNumberFormatterWrapper.get() )
1200             aNullDate = m_apNumberFormatterWrapper->getNullDate();
1201         if( aIt!=aEnd )
1202         {
1203             Date aPrevious(aNullDate); aPrevious+=static_cast<long>(rtl::math::approxFloor(aIt->fValue));
1204             ++aIt;
1205             for(;aIt!=aEnd;++aIt)
1206             {
1207                 Date aCurrent(aNullDate); aCurrent+=static_cast<long>(rtl::math::approxFloor(aIt->fValue));
1208                 if( ::com::sun::star::chart::TimeUnit::YEAR == nRet )
1209                 {
1210                     if( DateHelper::IsInSameYear( aPrevious, aCurrent ) )
1211                         nRet = ::com::sun::star::chart::TimeUnit::MONTH;
1212                 }
1213                 if( ::com::sun::star::chart::TimeUnit::MONTH == nRet )
1214                 {
1215                     if( DateHelper::IsInSameMonth( aPrevious, aCurrent ) )
1216                         nRet = ::com::sun::star::chart::TimeUnit::DAY;
1217                 }
1218                 if( ::com::sun::star::chart::TimeUnit::DAY == nRet )
1219                     break;
1220                 aPrevious=aCurrent;
1221             }
1222         }
1223     }
1224     return nRet;
1225 }
getMinimumX()1226 double VSeriesPlotter::getMinimumX()
1227 {
1228     double fMinimum, fMaximum;
1229     this->getMinimumAndMaximiumX( fMinimum, fMaximum );
1230     return fMinimum;
1231 }
getMaximumX()1232 double VSeriesPlotter::getMaximumX()
1233 {
1234     double fMinimum, fMaximum;
1235     this->getMinimumAndMaximiumX( fMinimum, fMaximum );
1236     return fMaximum;
1237 }
1238 
getMinimumYInRange(double fMinimumX,double fMaximumX,sal_Int32 nAxisIndex)1239 double VSeriesPlotter::getMinimumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex )
1240 {
1241     if( !m_bCategoryXAxis || ( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() ) )
1242     {
1243         double fMinY, fMaxY;
1244         this->getMinimumAndMaximiumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex );
1245         return fMinY;
1246     }
1247 
1248     double fMinimum, fMaximum;
1249     ::rtl::math::setInf(&fMinimum, false);
1250     ::rtl::math::setInf(&fMaximum, true);
1251     for(size_t nZ =0; nZ<m_aZSlots.size();nZ++ )
1252     {
1253         ::std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[nZ];
1254         for(size_t nN =0; nN<rXSlots.size();nN++ )
1255         {
1256             double fLocalMinimum, fLocalMaximum;
1257             rXSlots[nN].calculateYMinAndMaxForCategoryRange(
1258                                 static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0
1259                                 , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0
1260                                 , isSeperateStackingForDifferentSigns( 1 )
1261                                 , fLocalMinimum, fLocalMaximum, nAxisIndex );
1262             if(fMaximum<fLocalMaximum)
1263                 fMaximum=fLocalMaximum;
1264             if(fMinimum>fLocalMinimum)
1265                 fMinimum=fLocalMinimum;
1266         }
1267     }
1268     if(::rtl::math::isInf(fMinimum))
1269         ::rtl::math::setNan(&fMinimum);
1270     return fMinimum;
1271 }
1272 
getMaximumYInRange(double fMinimumX,double fMaximumX,sal_Int32 nAxisIndex)1273 double VSeriesPlotter::getMaximumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex )
1274 {
1275     if( !m_bCategoryXAxis || ( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() ) )
1276     {
1277         double fMinY, fMaxY;
1278         this->getMinimumAndMaximiumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex );
1279         return fMaxY;
1280     }
1281 
1282     double fMinimum, fMaximum;
1283     ::rtl::math::setInf(&fMinimum, false);
1284     ::rtl::math::setInf(&fMaximum, true);
1285     for(size_t nZ =0; nZ<m_aZSlots.size();nZ++ )
1286     {
1287         ::std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[nZ];
1288         for(size_t nN =0; nN<rXSlots.size();nN++ )
1289         {
1290             double fLocalMinimum, fLocalMaximum;
1291             rXSlots[nN].calculateYMinAndMaxForCategoryRange(
1292                                 static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0
1293                                 , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0
1294                                 , isSeperateStackingForDifferentSigns( 1 )
1295                                 , fLocalMinimum, fLocalMaximum, nAxisIndex );
1296             if(fMaximum<fLocalMaximum)
1297                 fMaximum=fLocalMaximum;
1298             if(fMinimum>fLocalMinimum)
1299                 fMinimum=fLocalMinimum;
1300         }
1301     }
1302     if(::rtl::math::isInf(fMaximum))
1303         ::rtl::math::setNan(&fMaximum);
1304     return fMaximum;
1305 }
1306 
getMinimumZ()1307 double VSeriesPlotter::getMinimumZ()
1308 {
1309     //this is the default for all charts without a meaningful z axis
1310     return 1.0;
1311 }
getMaximumZ()1312 double VSeriesPlotter::getMaximumZ()
1313 {
1314     if( 3!=m_nDimension || !m_aZSlots.size() )
1315         return getMinimumZ()+1;
1316     return m_aZSlots.size();
1317 }
1318 
1319 namespace
1320 {
lcl_isValueAxis(sal_Int32 nDimensionIndex,bool bCategoryXAxis)1321     bool lcl_isValueAxis( sal_Int32 nDimensionIndex, bool bCategoryXAxis )
1322     {
1323         // default implementation: true for Y axes, and for value X axis
1324         if( nDimensionIndex == 0 )
1325             return !bCategoryXAxis;
1326         if( nDimensionIndex == 1 )
1327             return true;
1328         return false;
1329     }
1330 }
1331 
isExpandBorderToIncrementRhythm(sal_Int32 nDimensionIndex)1332 bool VSeriesPlotter::isExpandBorderToIncrementRhythm( sal_Int32 nDimensionIndex )
1333 {
1334     return lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis );
1335 }
1336 
isExpandIfValuesCloseToBorder(sal_Int32 nDimensionIndex)1337 bool VSeriesPlotter::isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex )
1338 {
1339     // do not expand axes in 3D charts
1340     return (m_nDimension < 3) && lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis );
1341 }
1342 
isExpandWideValuesToZero(sal_Int32 nDimensionIndex)1343 bool VSeriesPlotter::isExpandWideValuesToZero( sal_Int32 nDimensionIndex )
1344 {
1345     // default implementation: only for Y axis
1346     return nDimensionIndex == 1;
1347 }
1348 
isExpandNarrowValuesTowardZero(sal_Int32 nDimensionIndex)1349 bool VSeriesPlotter::isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex )
1350 {
1351     // default implementation: only for Y axis
1352     return nDimensionIndex == 1;
1353 }
1354 
isSeperateStackingForDifferentSigns(sal_Int32 nDimensionIndex)1355 bool VSeriesPlotter::isSeperateStackingForDifferentSigns( sal_Int32 nDimensionIndex )
1356 {
1357     // default implementation: only for Y axis
1358     return nDimensionIndex == 1;
1359 }
1360 
getMinimumAndMaximiumX(double & rfMinimum,double & rfMaximum) const1361 void VSeriesPlotter::getMinimumAndMaximiumX( double& rfMinimum, double& rfMaximum ) const
1362 {
1363     ::rtl::math::setInf(&rfMinimum, false);
1364     ::rtl::math::setInf(&rfMaximum, true);
1365 
1366     ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator       aZSlotIter = m_aZSlots.begin();
1367     const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator  aZSlotEnd = m_aZSlots.end();
1368     for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ )
1369     {
1370         ::std::vector< VDataSeriesGroup >::const_iterator      aXSlotIter = aZSlotIter->begin();
1371         const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1372         for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ )
1373         {
1374             double fLocalMinimum, fLocalMaximum;
1375             aXSlotIter->getMinimumAndMaximiumX( fLocalMinimum, fLocalMaximum );
1376             if( !::rtl::math::isNan(fLocalMinimum) && fLocalMinimum< rfMinimum )
1377                 rfMinimum = fLocalMinimum;
1378             if( !::rtl::math::isNan(fLocalMaximum) && fLocalMaximum> rfMaximum )
1379                 rfMaximum = fLocalMaximum;
1380         }
1381     }
1382     if(::rtl::math::isInf(rfMinimum))
1383         ::rtl::math::setNan(&rfMinimum);
1384     if(::rtl::math::isInf(rfMaximum))
1385         ::rtl::math::setNan(&rfMaximum);
1386 }
1387 
getMinimumAndMaximiumYInContinuousXRange(double & rfMinY,double & rfMaxY,double fMinX,double fMaxX,sal_Int32 nAxisIndex) const1388 void VSeriesPlotter::getMinimumAndMaximiumYInContinuousXRange( double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const
1389 {
1390     ::rtl::math::setInf(&rfMinY, false);
1391     ::rtl::math::setInf(&rfMaxY, true);
1392 
1393     ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator       aZSlotIter = m_aZSlots.begin();
1394     const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator  aZSlotEnd = m_aZSlots.end();
1395     for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ )
1396     {
1397         ::std::vector< VDataSeriesGroup >::const_iterator      aXSlotIter = aZSlotIter->begin();
1398         const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1399         for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ )
1400         {
1401             double fLocalMinimum, fLocalMaximum;
1402             aXSlotIter->getMinimumAndMaximiumYInContinuousXRange( fLocalMinimum, fLocalMaximum, fMinX, fMaxX, nAxisIndex );
1403             if( !::rtl::math::isNan(fLocalMinimum) && fLocalMinimum< rfMinY )
1404                 rfMinY = fLocalMinimum;
1405             if( !::rtl::math::isNan(fLocalMaximum) && fLocalMaximum> rfMaxY )
1406                 rfMaxY = fLocalMaximum;
1407         }
1408     }
1409     if(::rtl::math::isInf(rfMinY))
1410         ::rtl::math::setNan(&rfMinY);
1411     if(::rtl::math::isInf(rfMaxY))
1412         ::rtl::math::setNan(&rfMaxY);
1413 }
1414 
getPointCount() const1415 sal_Int32 VSeriesPlotter::getPointCount() const
1416 {
1417     sal_Int32 nRet = 0;
1418 
1419     ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator       aZSlotIter = m_aZSlots.begin();
1420     const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
1421 
1422     for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ )
1423     {
1424         ::std::vector< VDataSeriesGroup >::const_iterator       aXSlotIter = aZSlotIter->begin();
1425         const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1426 
1427         for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ )
1428         {
1429             sal_Int32 nPointCount = aXSlotIter->getPointCount();
1430             if( nPointCount>nRet )
1431                 nRet = nPointCount;
1432         }
1433     }
1434     return nRet;
1435 }
1436 
setNumberFormatsSupplier(const uno::Reference<util::XNumberFormatsSupplier> & xNumFmtSupplier)1437 void VSeriesPlotter::setNumberFormatsSupplier(
1438     const uno::Reference< util::XNumberFormatsSupplier > & xNumFmtSupplier )
1439 {
1440     m_apNumberFormatterWrapper.reset( new NumberFormatterWrapper( xNumFmtSupplier ));
1441 }
1442 
setColorScheme(const uno::Reference<XColorScheme> & xColorScheme)1443 void VSeriesPlotter::setColorScheme( const uno::Reference< XColorScheme >& xColorScheme )
1444 {
1445     m_xColorScheme = xColorScheme;
1446 }
1447 
setExplicitCategoriesProvider(ExplicitCategoriesProvider * pExplicitCategoriesProvider)1448 void VSeriesPlotter::setExplicitCategoriesProvider( ExplicitCategoriesProvider* pExplicitCategoriesProvider )
1449 {
1450     m_pExplicitCategoriesProvider = pExplicitCategoriesProvider;
1451 }
1452 
getPointCount() const1453 sal_Int32 VDataSeriesGroup::getPointCount() const
1454 {
1455     if(!m_bMaxPointCountDirty)
1456         return m_nMaxPointCount;
1457 
1458     sal_Int32 nRet = 0;
1459     ::std::vector< VDataSeries* >::const_iterator       aSeriesIter = m_aSeriesVector.begin();
1460     const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = m_aSeriesVector.end();
1461 
1462     for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ )
1463     {
1464         sal_Int32 nPointCount = (*aSeriesIter)->getTotalPointCount();
1465         if( nPointCount>nRet )
1466             nRet = nPointCount;
1467     }
1468     m_nMaxPointCount=nRet;
1469     m_aListOfCachedYValues.clear();
1470     m_aListOfCachedYValues.resize(m_nMaxPointCount);
1471     m_bMaxPointCountDirty=false;
1472     return nRet;
1473 }
1474 
getAttachedAxisIndexForFirstSeries() const1475 sal_Int32 VDataSeriesGroup::getAttachedAxisIndexForFirstSeries() const
1476 {
1477     sal_Int32 nRet = 0;
1478     ::std::vector< VDataSeries* >::const_iterator       aSeriesIter = m_aSeriesVector.begin();
1479     const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = m_aSeriesVector.end();
1480 
1481     if( aSeriesIter != aSeriesEnd )
1482         nRet = (*aSeriesIter)->getAttachedAxisIndex();
1483 
1484     return nRet;
1485 }
1486 
getMinimumAndMaximiumX(double & rfMinimum,double & rfMaximum) const1487 void VDataSeriesGroup::getMinimumAndMaximiumX( double& rfMinimum, double& rfMaximum ) const
1488 {
1489     const ::std::vector< VDataSeries* >* pSeriesList = &this->m_aSeriesVector;
1490 
1491     ::std::vector< VDataSeries* >::const_iterator       aSeriesIter = pSeriesList->begin();
1492     const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd  = pSeriesList->end();
1493 
1494     ::rtl::math::setInf(&rfMinimum, false);
1495     ::rtl::math::setInf(&rfMaximum, true);
1496 
1497 	for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ )
1498     {
1499         sal_Int32 nPointCount = (*aSeriesIter)->getTotalPointCount();
1500         for(sal_Int32 nN=0;nN<nPointCount;nN++)
1501         {
1502             double fX = (*aSeriesIter)->getXValue( nN );
1503             if( ::rtl::math::isNan(fX) )
1504                 continue;
1505             if(rfMaximum<fX)
1506                 rfMaximum=fX;
1507             if(rfMinimum>fX)
1508                 rfMinimum=fX;
1509         }
1510     }
1511     if(::rtl::math::isInf(rfMinimum))
1512         ::rtl::math::setNan(&rfMinimum);
1513     if(::rtl::math::isInf(rfMaximum))
1514         ::rtl::math::setNan(&rfMaximum);
1515 }
getMinimumAndMaximiumYInContinuousXRange(double & rfMinY,double & rfMaxY,double fMinX,double fMaxX,sal_Int32 nAxisIndex) const1516 void VDataSeriesGroup::getMinimumAndMaximiumYInContinuousXRange( double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const
1517 {
1518     const ::std::vector< VDataSeries* >* pSeriesList = &this->m_aSeriesVector;
1519 
1520     ::std::vector< VDataSeries* >::const_iterator       aSeriesIter = pSeriesList->begin();
1521     const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd  = pSeriesList->end();
1522 
1523     ::rtl::math::setInf(&rfMinY, false);
1524     ::rtl::math::setInf(&rfMaxY, true);
1525 
1526 	for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ )
1527     {
1528         sal_Int32 nPointCount = (*aSeriesIter)->getTotalPointCount();
1529         for(sal_Int32 nN=0;nN<nPointCount;nN++)
1530         {
1531             if( nAxisIndex != (*aSeriesIter)->getAttachedAxisIndex() )
1532                 continue;
1533 
1534             double fX = (*aSeriesIter)->getXValue( nN );
1535             if( ::rtl::math::isNan(fX) )
1536                 continue;
1537             if( fX < fMinX || fX > fMaxX )
1538                 continue;
1539             double fY = (*aSeriesIter)->getYValue( nN );
1540 
1541             if( ::rtl::math::isNan(fY) )
1542                 continue;
1543             if(rfMaxY<fY)
1544                 rfMaxY=fY;
1545             if(rfMinY>fY)
1546                 rfMinY=fY;
1547 			////for stockchart with candlestick start
1548 			//todo:stockchart with candlestick has no values-y but values-min,values-max,values-first and values-last
1549 			//also check values-min,values-max for max and min value
1550 			double fYmax = (*aSeriesIter)->getY_Max( nN );
1551 			double fYmin = (*aSeriesIter)->getY_Min( nN );
1552 			if(!::rtl::math::isNan(fYmax))
1553 				if( rfMaxY<fYmax) rfMaxY=fYmax;
1554 			if(!::rtl::math::isNan(fYmin))
1555 				if( rfMinY>fYmin) rfMinY=fYmin;
1556 			////for stockchart with candlestick end
1557         }
1558     }
1559     if(::rtl::math::isInf(rfMinY))
1560         ::rtl::math::setNan(&rfMinY);
1561     if(::rtl::math::isInf(rfMaxY))
1562         ::rtl::math::setNan(&rfMaxY);
1563 }
1564 
calculateYMinAndMaxForCategory(sal_Int32 nCategoryIndex,bool bSeperateStackingForDifferentSigns,double & rfMinimumY,double & rfMaximumY,sal_Int32 nAxisIndex)1565 void VDataSeriesGroup::calculateYMinAndMaxForCategory( sal_Int32 nCategoryIndex
1566         , bool bSeperateStackingForDifferentSigns
1567         , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex )
1568 {
1569     ::rtl::math::setInf(&rfMinimumY, false);
1570     ::rtl::math::setInf(&rfMaximumY, true);
1571 
1572     sal_Int32 nPointCount = getPointCount();//necessary to create m_aListOfCachedYValues
1573     if(nCategoryIndex<0 || nCategoryIndex>=nPointCount || m_aSeriesVector.empty())
1574         return;
1575 
1576     CachedYValues aCachedYValues = m_aListOfCachedYValues[nCategoryIndex][nAxisIndex];
1577     if( !aCachedYValues.m_bValuesDirty )
1578     {
1579         //return cached values
1580         rfMinimumY = aCachedYValues.m_fMinimumY;
1581         rfMaximumY = aCachedYValues.m_fMaximumY;
1582         return;
1583     }
1584 
1585     double fTotalSum, fPositiveSum, fNegativeSum, fFirstPositiveY, fFirstNegativeY;
1586     ::rtl::math::setNan( &fTotalSum );
1587     ::rtl::math::setNan( &fPositiveSum );
1588     ::rtl::math::setNan( &fNegativeSum );
1589     ::rtl::math::setNan( &fFirstPositiveY );
1590     ::rtl::math::setNan( &fFirstNegativeY );
1591 
1592     ::std::vector< VDataSeries* >::const_iterator aSeriesIter = m_aSeriesVector.begin();
1593     ::std::vector< VDataSeries* >::const_iterator aSeriesEnd  = m_aSeriesVector.end();
1594 
1595     if( bSeperateStackingForDifferentSigns )
1596     {
1597         for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter )
1598         {
1599             if( nAxisIndex != (*aSeriesIter)->getAttachedAxisIndex() )
1600                 continue;
1601 
1602             double fValueMinY = (*aSeriesIter)->getMinimumofAllDifferentYValues( nCategoryIndex );
1603             double fValueMaxY = (*aSeriesIter)->getMaximumofAllDifferentYValues( nCategoryIndex );
1604 
1605             if( fValueMaxY >= 0 )
1606             {
1607                 if( ::rtl::math::isNan( fPositiveSum ) )
1608                     fPositiveSum = fFirstPositiveY = fValueMaxY;
1609                 else
1610                     fPositiveSum += fValueMaxY;
1611             }
1612             if( fValueMinY < 0 )
1613             {
1614                 if(::rtl::math::isNan( fNegativeSum ))
1615                     fNegativeSum = fFirstNegativeY = fValueMinY;
1616                 else
1617                     fNegativeSum += fValueMinY;
1618             }
1619         }
1620         rfMinimumY = ::rtl::math::isNan( fNegativeSum ) ? fFirstPositiveY : fNegativeSum;
1621         rfMaximumY = ::rtl::math::isNan( fPositiveSum ) ? fFirstNegativeY : fPositiveSum;
1622     }
1623     else
1624     {
1625         for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter )
1626         {
1627             if( nAxisIndex != (*aSeriesIter)->getAttachedAxisIndex() )
1628                 continue;
1629 
1630             double fValueMinY = (*aSeriesIter)->getMinimumofAllDifferentYValues( nCategoryIndex );
1631             double fValueMaxY = (*aSeriesIter)->getMaximumofAllDifferentYValues( nCategoryIndex );
1632 
1633             if( ::rtl::math::isNan( fTotalSum ) )
1634             {
1635                 rfMinimumY = fValueMinY;
1636                 rfMaximumY = fTotalSum = fValueMaxY;
1637             }
1638             else
1639             {
1640                 fTotalSum += fValueMaxY;
1641                 if( rfMinimumY > fTotalSum )
1642                     rfMinimumY = fTotalSum;
1643                 if( rfMaximumY < fTotalSum )
1644                     rfMaximumY = fTotalSum;
1645             }
1646         }
1647     }
1648 
1649     aCachedYValues.m_fMinimumY = rfMinimumY;
1650     aCachedYValues.m_fMaximumY = rfMaximumY;
1651     aCachedYValues.m_bValuesDirty = false;
1652     m_aListOfCachedYValues[nCategoryIndex][nAxisIndex]=aCachedYValues;
1653 }
1654 
calculateYMinAndMaxForCategoryRange(sal_Int32 nStartCategoryIndex,sal_Int32 nEndCategoryIndex,bool bSeperateStackingForDifferentSigns,double & rfMinimumY,double & rfMaximumY,sal_Int32 nAxisIndex)1655 void VDataSeriesGroup::calculateYMinAndMaxForCategoryRange(
1656         sal_Int32 nStartCategoryIndex, sal_Int32 nEndCategoryIndex
1657         , bool bSeperateStackingForDifferentSigns
1658         , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex )
1659 {
1660     //@todo maybe cache these values
1661     ::rtl::math::setInf(&rfMinimumY, false);
1662     ::rtl::math::setInf(&rfMaximumY, true);
1663 
1664     //iterate through the given categories
1665     if(nStartCategoryIndex<0)
1666         nStartCategoryIndex=0;
1667     if(nEndCategoryIndex<0)
1668         nEndCategoryIndex=0;
1669     for( sal_Int32 nCatIndex = nStartCategoryIndex; nCatIndex <= nEndCategoryIndex; nCatIndex++ )
1670 	{
1671         double fMinimumY; ::rtl::math::setNan(&fMinimumY);
1672         double fMaximumY; ::rtl::math::setNan(&fMaximumY);
1673 
1674         this->calculateYMinAndMaxForCategory( nCatIndex
1675             , bSeperateStackingForDifferentSigns, fMinimumY, fMaximumY, nAxisIndex );
1676 
1677         if(rfMinimumY > fMinimumY)
1678             rfMinimumY = fMinimumY;
1679         if(rfMaximumY < fMaximumY)
1680             rfMaximumY = fMaximumY;
1681     }
1682 }
1683 
getTransformedDepth() const1684 double VSeriesPlotter::getTransformedDepth() const
1685 {
1686     double MinZ = m_pMainPosHelper->getLogicMinZ();
1687     double MaxZ = m_pMainPosHelper->getLogicMaxZ();
1688     m_pMainPosHelper->doLogicScaling( 0, 0, &MinZ );
1689     m_pMainPosHelper->doLogicScaling( 0, 0, &MaxZ );
1690     return FIXED_SIZE_FOR_3D_CHART_VOLUME/(MaxZ-MinZ);
1691 }
1692 
addSecondaryValueScale(const ExplicitScaleData & rScale,sal_Int32 nAxisIndex)1693 void VSeriesPlotter::addSecondaryValueScale( const ExplicitScaleData& rScale, sal_Int32 nAxisIndex )
1694                 throw (uno::RuntimeException)
1695 {
1696     if( nAxisIndex<1 )
1697         return;
1698 
1699     m_aSecondaryValueScales[nAxisIndex]=rScale;
1700 }
1701 
getPlottingPositionHelper(sal_Int32 nAxisIndex) const1702 PlottingPositionHelper& VSeriesPlotter::getPlottingPositionHelper( sal_Int32 nAxisIndex ) const
1703 {
1704     PlottingPositionHelper* pRet = 0;
1705     if(nAxisIndex>0)
1706     {
1707         tSecondaryPosHelperMap::const_iterator aPosIt = m_aSecondaryPosHelperMap.find( nAxisIndex );
1708         if( aPosIt != m_aSecondaryPosHelperMap.end() )
1709         {
1710             pRet = aPosIt->second;
1711         }
1712         else
1713         {
1714             tSecondaryValueScales::const_iterator aScaleIt = m_aSecondaryValueScales.find( nAxisIndex );
1715             if( aScaleIt != m_aSecondaryValueScales.end() )
1716             {
1717                 pRet = m_pPosHelper->createSecondaryPosHelper( aScaleIt->second );
1718                 m_aSecondaryPosHelperMap[nAxisIndex] = pRet;
1719             }
1720         }
1721     }
1722     if( !pRet )
1723         pRet = m_pMainPosHelper;
1724     if(pRet)
1725         pRet->setTimeResolution( m_nTimeResolution, m_aNullDate );
1726     return *pRet;
1727 }
1728 
rearrangeLabelToAvoidOverlapIfRequested(const awt::Size &)1729 void VSeriesPlotter::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& /*rPageSize*/ )
1730 {
1731 }
1732 
getFirstSeries() const1733 VDataSeries* VSeriesPlotter::getFirstSeries() const
1734 {
1735     ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter( m_aZSlots.begin() );
1736     ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd( m_aZSlots.end() );
1737     for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
1738     {
1739         ::std::vector< VDataSeriesGroup >::const_iterator       aXSlotIter = aZSlotIter->begin();
1740         const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd  = aZSlotIter->end();
1741 
1742         if( aXSlotIter != aXSlotEnd )
1743         {
1744             VDataSeriesGroup aSeriesGroup( *aXSlotIter );
1745             if( aSeriesGroup.m_aSeriesVector.size() )
1746             {
1747                 VDataSeries* pSeries = aSeriesGroup.m_aSeriesVector[0];
1748                 if(pSeries)
1749                     return pSeries;
1750             }
1751         }
1752     }
1753     return 0;
1754 }
1755 
getSeriesNames() const1756 uno::Sequence< rtl::OUString > VSeriesPlotter::getSeriesNames() const
1757 {
1758     ::std::vector< rtl::OUString > aRetVector;
1759 
1760     rtl::OUString aRole;
1761     if( m_xChartTypeModel.is() )
1762         aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel();
1763 
1764     ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter( m_aZSlots.begin() );
1765     ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd( m_aZSlots.end() );
1766     for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter )
1767     {
1768         ::std::vector< VDataSeriesGroup >::const_iterator       aXSlotIter = aZSlotIter->begin();
1769         const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd  = aZSlotIter->end();
1770 
1771         if( aXSlotIter != aXSlotEnd )
1772         {
1773             VDataSeriesGroup aSeriesGroup( *aXSlotIter );
1774             if( aSeriesGroup.m_aSeriesVector.size() )
1775             {
1776                 VDataSeries* pSeries = aSeriesGroup.m_aSeriesVector[0];
1777                 uno::Reference< XDataSeries > xSeries( pSeries ? pSeries->getModel() : 0 );
1778                 if( xSeries.is() )
1779                 {
1780                     rtl::OUString aSeriesName( DataSeriesHelper::getDataSeriesLabel( xSeries, aRole ) );
1781                     aRetVector.push_back( aSeriesName );
1782                 }
1783             }
1784         }
1785     }
1786     return ContainerHelper::ContainerToSequence( aRetVector );
1787 }
1788 
1789 namespace
1790 {
1791 struct lcl_setRefSizeAtSeriesGroup : public ::std::unary_function< VDataSeriesGroup, void >
1792 {
lcl_setRefSizeAtSeriesGroupchart::__anon84b939cd0311::lcl_setRefSizeAtSeriesGroup1793     lcl_setRefSizeAtSeriesGroup( awt::Size aRefSize ) : m_aRefSize( aRefSize ) {}
operator ()chart::__anon84b939cd0311::lcl_setRefSizeAtSeriesGroup1794     void operator()( VDataSeriesGroup & rGroup )
1795     {
1796         ::std::vector< VDataSeries* >::iterator aIt( rGroup.m_aSeriesVector.begin());
1797         const ::std::vector< VDataSeries* >::iterator aEndIt( rGroup.m_aSeriesVector.end());
1798         for( ; aIt != aEndIt; ++aIt )
1799             (*aIt)->setPageReferenceSize( m_aRefSize );
1800     }
1801 
1802 private:
1803     awt::Size m_aRefSize;
1804 };
1805 } // anonymous namespace
1806 
setPageReferenceSize(const::com::sun::star::awt::Size & rPageRefSize)1807 void VSeriesPlotter::setPageReferenceSize( const ::com::sun::star::awt::Size & rPageRefSize )
1808 {
1809     m_aPageReferenceSize = rPageRefSize;
1810 
1811     // set reference size also at all data series
1812 
1813     ::std::vector< VDataSeriesGroup > aSeriesGroups( FlattenVector( m_aZSlots ));
1814     ::std::for_each( aSeriesGroups.begin(), aSeriesGroups.end(),
1815                      lcl_setRefSizeAtSeriesGroup( m_aPageReferenceSize ));
1816 }
1817 
1818 //better performance for big data
setCoordinateSystemResolution(const Sequence<sal_Int32> & rCoordinateSystemResolution)1819 void VSeriesPlotter::setCoordinateSystemResolution( const Sequence< sal_Int32 >& rCoordinateSystemResolution )
1820 {
1821     m_aCoordinateSystemResolution = rCoordinateSystemResolution;
1822 }
1823 
PointsWereSkipped() const1824 bool VSeriesPlotter::PointsWereSkipped() const
1825 {
1826     return m_bPointsWereSkipped;
1827 }
1828 
WantToPlotInFrontOfAxisLine()1829 bool VSeriesPlotter::WantToPlotInFrontOfAxisLine()
1830 {
1831     return ChartTypeHelper::isSeriesInFrontOfAxisLine( m_xChartTypeModel );
1832 }
1833 
shouldSnapRectToUsedArea()1834 bool VSeriesPlotter::shouldSnapRectToUsedArea()
1835 {
1836     if( m_nDimension == 3 )
1837         return false;
1838     return true;
1839 }
1840 
createLegendEntries(const awt::Size & rEntryKeyAspectRatio,::com::sun::star::chart::ChartLegendExpansion eLegendExpansion,const Reference<beans::XPropertySet> & xTextProperties,const Reference<drawing::XShapes> & xTarget,const Reference<lang::XMultiServiceFactory> & xShapeFactory,const Reference<uno::XComponentContext> & xContext)1841 std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntries(
1842               const awt::Size& rEntryKeyAspectRatio
1843             , ::com::sun::star::chart::ChartLegendExpansion eLegendExpansion
1844             , const Reference< beans::XPropertySet >& xTextProperties
1845             , const Reference< drawing::XShapes >& xTarget
1846             , const Reference< lang::XMultiServiceFactory >& xShapeFactory
1847             , const Reference< uno::XComponentContext >& xContext
1848             )
1849 {
1850     std::vector< ViewLegendEntry > aResult;
1851 
1852     if( xTarget.is() )
1853     {
1854         //iterate through all series
1855         bool bBreak = false;
1856         bool bFirstSeries = true;
1857         ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator             aZSlotIter = m_aZSlots.begin();
1858         const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator  aZSlotEnd = m_aZSlots.end();
1859         for( ; aZSlotIter!=aZSlotEnd && !bBreak; aZSlotIter++ )
1860         {
1861             ::std::vector< VDataSeriesGroup >::iterator             aXSlotIter = aZSlotIter->begin();
1862             const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1863             for( ; aXSlotIter!=aXSlotEnd && !bBreak; aXSlotIter++ )
1864             {
1865                 ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
1866                 ::std::vector< VDataSeries* >::const_iterator       aSeriesIter = pSeriesList->begin();
1867                 const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd  = pSeriesList->end();
1868                 //iterate through all series in this x slot
1869 	            for( ; aSeriesIter!=aSeriesEnd && !bBreak; ++aSeriesIter )
1870                 {
1871                     VDataSeries* pSeries( *aSeriesIter );
1872                     if(!pSeries)
1873                         continue;
1874 
1875                     std::vector< ViewLegendEntry > aSeriesEntries( this->createLegendEntriesForSeries( rEntryKeyAspectRatio,
1876                             *pSeries, xTextProperties, xTarget, xShapeFactory, xContext ) );
1877 
1878                     //add series entries to the result now
1879 
1880                     // use only the first series if VaryColorsByPoint is set for the first series
1881                     if( bFirstSeries && pSeries->isVaryColorsByPoint() )
1882                         bBreak = true;
1883                     bFirstSeries = false;
1884 
1885                     // add entries reverse if chart is stacked in y-direction and the legend is not wide.
1886                     // If the legend is wide and we have a stacked bar-chart the normal order
1887                     // is the correct one
1888                     bool bReverse = false;
1889                     if( eLegendExpansion != ::com::sun::star::chart::ChartLegendExpansion_WIDE )
1890                     {
1891                         StackingDirection eStackingDirection( pSeries->getStackingDirection() );
1892                         bReverse = ( eStackingDirection == StackingDirection_Y_STACKING );
1893 
1894                         //todo: respect direction of axis in future
1895                     }
1896 
1897                     if(bReverse)
1898                         aResult.insert( aResult.begin(), aSeriesEntries.begin(), aSeriesEntries.end() );
1899                     else
1900                         aResult.insert( aResult.end(), aSeriesEntries.begin(), aSeriesEntries.end() );
1901                 }
1902             }
1903         }
1904     }
1905 
1906     return aResult;
1907 }
1908 
getAllSeries()1909 ::std::vector< VDataSeries* > VSeriesPlotter::getAllSeries()
1910 {
1911     ::std::vector< VDataSeries* > aAllSeries;
1912     ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator            aZSlotIter = m_aZSlots.begin();
1913     const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end();
1914     for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ )
1915     {
1916         ::std::vector< VDataSeriesGroup >::iterator             aXSlotIter = aZSlotIter->begin();
1917         const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end();
1918         for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ )
1919 	    {
1920             ::std::vector< VDataSeries* > aSeriesList = aXSlotIter->m_aSeriesVector;
1921             aAllSeries.insert( aAllSeries.end(), aSeriesList.begin(), aSeriesList.end() );
1922         }
1923     }
1924     return aAllSeries;
1925 }
1926 
1927 namespace
1928 {
lcl_HasVisibleLine(const uno::Reference<beans::XPropertySet> & xProps,bool & rbHasDashedLine)1929 bool lcl_HasVisibleLine( const uno::Reference< beans::XPropertySet >& xProps, bool& rbHasDashedLine )
1930 {
1931     bool bHasVisibleLine = false;
1932     rbHasDashedLine = false;
1933     drawing::LineStyle aLineStyle = drawing::LineStyle_NONE;
1934     if( xProps.is() && ( xProps->getPropertyValue( C2U("LineStyle")) >>= aLineStyle ) )
1935     {
1936         if( aLineStyle != drawing::LineStyle_NONE )
1937             bHasVisibleLine = true;
1938         if( aLineStyle == drawing::LineStyle_DASH )
1939             rbHasDashedLine = true;
1940     }
1941     return bHasVisibleLine;
1942 }
1943 
lcl_HasRegressionCurves(const VDataSeries & rSeries,bool & rbHasDashedLine)1944 bool lcl_HasRegressionCurves( const VDataSeries& rSeries, bool& rbHasDashedLine )
1945 {
1946     bool bHasRegressionCurves = false;
1947     Reference< XRegressionCurveContainer > xRegrCont( rSeries.getModel(), uno::UNO_QUERY );
1948     if( xRegrCont.is())
1949     {
1950         Sequence< Reference< XRegressionCurve > > aCurves( xRegrCont->getRegressionCurves() );
1951         sal_Int32 i = 0, nCount = aCurves.getLength();
1952         for( i=0; i<nCount; ++i )
1953         {
1954             if( aCurves[i].is() )
1955             {
1956                 bHasRegressionCurves = true;
1957                 lcl_HasVisibleLine( uno::Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ), rbHasDashedLine );
1958             }
1959         }
1960     }
1961     return bHasRegressionCurves;
1962 }
1963 }
getLegendSymbolStyle()1964 LegendSymbolStyle VSeriesPlotter::getLegendSymbolStyle()
1965 {
1966     return LegendSymbolStyle_BOX;
1967 }
1968 
getPreferredLegendKeyAspectRatio()1969 awt::Size VSeriesPlotter::getPreferredLegendKeyAspectRatio()
1970 {
1971     awt::Size aRet(1000,1000);
1972     if( m_nDimension==3 )
1973         return aRet;
1974 
1975     bool bSeriesAllowsLines = (getLegendSymbolStyle() == LegendSymbolStyle_LINE);
1976     bool bHasLines = false;
1977     bool bHasDashedLines = false;
1978     ::std::vector< VDataSeries* > aAllSeries( getAllSeries() );
1979     ::std::vector< VDataSeries* >::const_iterator       aSeriesIter = aAllSeries.begin();
1980     const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd  = aAllSeries.end();
1981     //iterate through all series
1982     for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ )
1983     {
1984         if( bSeriesAllowsLines )
1985         {
1986             bool bCurrentDashed = false;
1987             if( lcl_HasVisibleLine( (*aSeriesIter)->getPropertiesOfSeries(), bCurrentDashed ) )
1988             {
1989                 bHasLines = true;
1990                 if( bCurrentDashed )
1991                 {
1992                     bHasDashedLines = true;
1993                     break;
1994                 }
1995             }
1996         }
1997         bool bRegressionHasDashedLines=false;
1998         if( lcl_HasRegressionCurves( **aSeriesIter, bRegressionHasDashedLines ) )
1999         {
2000             bHasLines = true;
2001             if( bRegressionHasDashedLines )
2002             {
2003                 bHasDashedLines = true;
2004                 break;
2005             }
2006         }
2007     }
2008     if( bHasLines )
2009     {
2010         if( bHasDashedLines )
2011             aRet = awt::Size(1600,-1);
2012         else
2013             aRet = awt::Size(800,-1);
2014     }
2015     return aRet;
2016 }
2017 
getExplicitSymbol(const VDataSeries &,sal_Int32)2018 uno::Any VSeriesPlotter::getExplicitSymbol( const VDataSeries& /*rSeries*/, sal_Int32 /*nPointIndex*/ )
2019 {
2020     return uno::Any();
2021 }
2022 
createLegendSymbolForSeries(const awt::Size & rEntryKeyAspectRatio,const VDataSeries & rSeries,const Reference<drawing::XShapes> & xTarget,const Reference<lang::XMultiServiceFactory> & xShapeFactory)2023 Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForSeries(
2024                   const awt::Size& rEntryKeyAspectRatio
2025                 , const VDataSeries& rSeries
2026                 , const Reference< drawing::XShapes >& xTarget
2027                 , const Reference< lang::XMultiServiceFactory >& xShapeFactory )
2028 {
2029 
2030     LegendSymbolStyle eLegendSymbolStyle = this->getLegendSymbolStyle();
2031     uno::Any aExplicitSymbol( this->getExplicitSymbol( rSeries ) );
2032 
2033     VLegendSymbolFactory::tPropertyType ePropType =
2034         VLegendSymbolFactory::PROP_TYPE_FILLED_SERIES;
2035 
2036     // todo: maybe the property-style does not solely depend on the
2037     // legend-symbol type
2038     switch( eLegendSymbolStyle )
2039     {
2040         case LegendSymbolStyle_LINE:
2041             ePropType = VLegendSymbolFactory::PROP_TYPE_LINE_SERIES;
2042             break;
2043         default:
2044             break;
2045     };
2046     Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio,
2047         xTarget, eLegendSymbolStyle, xShapeFactory
2048             , rSeries.getPropertiesOfSeries(), ePropType, aExplicitSymbol ));
2049 
2050     return xShape;
2051 }
2052 
createLegendSymbolForPoint(const awt::Size & rEntryKeyAspectRatio,const VDataSeries & rSeries,sal_Int32 nPointIndex,const Reference<drawing::XShapes> & xTarget,const Reference<lang::XMultiServiceFactory> & xShapeFactory)2053 Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForPoint(
2054                   const awt::Size& rEntryKeyAspectRatio
2055                 , const VDataSeries& rSeries
2056                 , sal_Int32 nPointIndex
2057                 , const Reference< drawing::XShapes >& xTarget
2058                 , const Reference< lang::XMultiServiceFactory >& xShapeFactory )
2059 {
2060 
2061     LegendSymbolStyle eLegendSymbolStyle = this->getLegendSymbolStyle();
2062     uno::Any aExplicitSymbol( this->getExplicitSymbol(rSeries,nPointIndex) );
2063 
2064     VLegendSymbolFactory::tPropertyType ePropType =
2065         VLegendSymbolFactory::PROP_TYPE_FILLED_SERIES;
2066 
2067     // todo: maybe the property-style does not solely depend on the
2068     // legend-symbol type
2069     switch( eLegendSymbolStyle )
2070     {
2071         case LegendSymbolStyle_LINE:
2072             ePropType = VLegendSymbolFactory::PROP_TYPE_LINE_SERIES;
2073             break;
2074         default:
2075             break;
2076     };
2077 
2078     // the default properties for the data point are the data series properties.
2079     // If a data point has own attributes overwrite them
2080     Reference< beans::XPropertySet > xSeriesProps( rSeries.getPropertiesOfSeries() );
2081     Reference< beans::XPropertySet > xPointSet( xSeriesProps );
2082     if( rSeries.isAttributedDataPoint( nPointIndex ) )
2083         xPointSet.set( rSeries.getPropertiesOfPoint( nPointIndex ));
2084 
2085     // if a data point has no own color use a color fom the diagram's color scheme
2086     if( ! rSeries.hasPointOwnColor( nPointIndex ))
2087     {
2088         Reference< util::XCloneable > xCloneable( xPointSet,uno::UNO_QUERY );
2089         if( xCloneable.is() && m_xColorScheme.is() )
2090         {
2091             xPointSet.set( xCloneable->createClone(), uno::UNO_QUERY );
2092             Reference< container::XChild > xChild( xPointSet, uno::UNO_QUERY );
2093             if( xChild.is())
2094                 xChild->setParent( xSeriesProps );
2095 
2096             OSL_ASSERT( xPointSet.is());
2097             xPointSet->setPropertyValue(
2098                 C2U("Color"), uno::makeAny( m_xColorScheme->getColorByIndex( nPointIndex )));
2099         }
2100     }
2101 
2102     Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio,
2103         xTarget, eLegendSymbolStyle, xShapeFactory, xPointSet, ePropType, aExplicitSymbol ));
2104 
2105     return xShape;
2106 }
2107 
createLegendEntriesForSeries(const awt::Size & rEntryKeyAspectRatio,const VDataSeries & rSeries,const Reference<beans::XPropertySet> & xTextProperties,const Reference<drawing::XShapes> & xTarget,const Reference<lang::XMultiServiceFactory> & xShapeFactory,const Reference<uno::XComponentContext> & xContext)2108 std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries(
2109               const awt::Size& rEntryKeyAspectRatio
2110             , const VDataSeries& rSeries
2111             , const Reference< beans::XPropertySet >& xTextProperties
2112             , const Reference< drawing::XShapes >& xTarget
2113             , const Reference< lang::XMultiServiceFactory >& xShapeFactory
2114             , const Reference< uno::XComponentContext >& xContext
2115             )
2116 {
2117     std::vector< ViewLegendEntry > aResult;
2118 
2119     if( ! ( xShapeFactory.is() && xTarget.is() && xContext.is() ) )
2120         return aResult;
2121 
2122     try
2123     {
2124         ViewLegendEntry aEntry;
2125         OUString aLabelText;
2126         bool bVaryColorsByPoint = rSeries.isVaryColorsByPoint();
2127         if( bVaryColorsByPoint )
2128         {
2129             Sequence< OUString > aCategoryNames;
2130             if( m_pExplicitCategoriesProvider )
2131                 aCategoryNames = m_pExplicitCategoriesProvider->getSimpleCategories();
2132 
2133             for( sal_Int32 nIdx=0; nIdx<aCategoryNames.getLength(); ++nIdx )
2134             {
2135                 // symbol
2136                 uno::Reference< drawing::XShapes > xSymbolGroup( ShapeFactory(xShapeFactory).createGroup2D( xTarget ));
2137 
2138                 // create the symbol
2139                 Reference< drawing::XShape > xShape( this->createLegendSymbolForPoint( rEntryKeyAspectRatio,
2140                     rSeries, nIdx, xSymbolGroup, xShapeFactory ) );
2141 
2142                 // set CID to symbol for selection
2143                 if( xShape.is() )
2144                 {
2145                     aEntry.aSymbol = uno::Reference< drawing::XShape >( xSymbolGroup, uno::UNO_QUERY );
2146 
2147                     OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_DATA_POINT, nIdx ) );
2148                     aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) );
2149                     OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2150                     ShapeFactory::setShapeName( xShape, aCID );
2151                 }
2152 
2153                 // label
2154                 aLabelText = aCategoryNames[nIdx];
2155                 if( xShape.is() || !aLabelText.isEmpty() )
2156                 {
2157                     aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties );
2158                     aResult.push_back(aEntry);
2159                 }
2160             }
2161         }
2162         else
2163         {
2164             // symbol
2165             uno::Reference< drawing::XShapes > xSymbolGroup( ShapeFactory(xShapeFactory).createGroup2D( xTarget ));
2166 
2167             // create the symbol
2168             Reference< drawing::XShape > xShape( this->createLegendSymbolForSeries(
2169                 rEntryKeyAspectRatio, rSeries, xSymbolGroup, xShapeFactory ) );
2170 
2171             // set CID to symbol for selection
2172             if( xShape.is())
2173             {
2174                 aEntry.aSymbol = uno::Reference< drawing::XShape >( xSymbolGroup, uno::UNO_QUERY );
2175 
2176                 OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) );
2177                 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2178                 ShapeFactory::setShapeName( xShape, aCID );
2179             }
2180 
2181             // label
2182             aLabelText = ( DataSeriesHelper::getDataSeriesLabel( rSeries.getModel(), m_xChartTypeModel.is() ? m_xChartTypeModel->getRoleOfSequenceForSeriesLabel() : C2U("values-y")) );
2183             aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties );
2184 
2185             aResult.push_back(aEntry);
2186         }
2187 
2188         // regression curves
2189         if ( 3 == m_nDimension )  // #i63016#
2190             return aResult;
2191 
2192         Reference< XRegressionCurveContainer > xRegrCont( rSeries.getModel(), uno::UNO_QUERY );
2193         if( xRegrCont.is())
2194         {
2195             Sequence< Reference< XRegressionCurve > > aCurves( xRegrCont->getRegressionCurves());
2196             sal_Int32 i = 0, nCount = aCurves.getLength();
2197             for( i=0; i<nCount; ++i )
2198             {
2199                 if( aCurves[i].is() )
2200                 {
2201                     //label
2202                     OUString aResStr( RegressionCurveHelper::getUINameForRegressionCurve( aCurves[i] ) );
2203                     replaceParamterInString( aResStr, C2U("%SERIESNAME"), aLabelText );
2204                     aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aResStr, xTextProperties );
2205 
2206                     // symbol
2207                     uno::Reference< drawing::XShapes > xSymbolGroup( ShapeFactory(xShapeFactory).createGroup2D( xTarget ));
2208 
2209                     // create the symbol
2210                     Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio,
2211                         xSymbolGroup, LegendSymbolStyle_LINE, xShapeFactory,
2212                         Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ),
2213                         VLegendSymbolFactory::PROP_TYPE_LINE, uno::Any() ));
2214 
2215                     // set CID to symbol for selection
2216                     if( xShape.is())
2217                     {
2218                         aEntry.aSymbol = uno::Reference< drawing::XShape >( xSymbolGroup, uno::UNO_QUERY );
2219 
2220                         bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurves[i] );
2221                         ObjectType eObjectType = bAverageLine ? OBJECTTYPE_DATA_AVERAGE_LINE : OBJECTTYPE_DATA_CURVE;
2222                         OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( eObjectType, i ) );
2223                         aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) );
2224                         OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2225                         ShapeFactory::setShapeName( xShape, aCID );
2226                     }
2227 
2228                     aResult.push_back(aEntry);
2229                 }
2230             }
2231         }
2232     }
2233     catch( uno::Exception & ex )
2234     {
2235         ASSERT_EXCEPTION( ex );
2236     }
2237     return aResult;
2238 }
2239 
createSeriesPlotter(const uno::Reference<XChartType> & xChartTypeModel,sal_Int32 nDimensionCount,bool bExcludingPositioning)2240 VSeriesPlotter* VSeriesPlotter::createSeriesPlotter(
2241     const uno::Reference<XChartType>& xChartTypeModel
2242     , sal_Int32 nDimensionCount
2243     , bool bExcludingPositioning )
2244 {
2245     rtl::OUString aChartType = xChartTypeModel->getChartType();
2246 
2247     //@todo: in future the plotter should be instanciated via service factory
2248     VSeriesPlotter* pRet=NULL;
2249     if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_COLUMN ) )
2250         pRet = new BarChart(xChartTypeModel,nDimensionCount);
2251     else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_BAR ) )
2252         pRet = new BarChart(xChartTypeModel,nDimensionCount);
2253     else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_AREA ) )
2254         pRet = new AreaChart(xChartTypeModel,nDimensionCount,true);
2255 	else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_LINE ) )
2256         pRet = new AreaChart(xChartTypeModel,nDimensionCount,true,true);
2257     else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER) )
2258         pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true);
2259     else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE) )
2260         pRet = new BubbleChart(xChartTypeModel,nDimensionCount);
2261     else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
2262         pRet = new PieChart(xChartTypeModel,nDimensionCount, bExcludingPositioning );
2263     else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
2264         pRet = new AreaChart(xChartTypeModel,nDimensionCount,true,true,new PolarPlottingPositionHelper(),true,false,1,drawing::Direction3D(1,1,1) );
2265     else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
2266         pRet = new AreaChart(xChartTypeModel,nDimensionCount,true,false,new PolarPlottingPositionHelper(),true,false,1,drawing::Direction3D(1,1,1) );
2267     else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
2268         pRet = new CandleStickChart(xChartTypeModel,nDimensionCount);
2269     else
2270         pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true);
2271     return pRet;
2272 }
2273 
2274 //.............................................................................
2275 } //namespace chart
2276 //.............................................................................
2277