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