LibreOffice Module chart2 (master) 1
VSeriesPlotter.cxx
Go to the documentation of this file.
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 <cstddef>
21#include <limits>
22#include <memory>
23#include <VSeriesPlotter.hxx>
24#include <BaseGFXHelper.hxx>
25#include <VLineProperties.hxx>
26#include <ShapeFactory.hxx>
27#include <Diagram.hxx>
29#include <DataSeries.hxx>
31
32#include <CommonConverters.hxx>
34#include <FormattedString.hxx>
35#include <ObjectIdentifier.hxx>
36#include <StatisticsHelper.hxx>
39#include <ChartType.hxx>
40#include <ChartTypeHelper.hxx>
41#include <Clipping.hxx>
44#include <DataSeriesHelper.hxx>
50#include <DateHelper.hxx>
51#include <DiagramHelper.hxx>
52#include <defines.hxx>
53#include <ChartModel.hxx>
54
55//only for creation: @todo remove if all plotter are uno components and instantiated via servicefactory
56#include "BarChart.hxx"
57#include "PieChart.hxx"
58#include "AreaChart.hxx"
59#include "CandleStickChart.hxx"
60#include "BubbleChart.hxx"
61#include "NetChart.hxx"
62#include <unonames.hxx>
63#include <SpecialCharacters.hxx>
64
65#include <com/sun/star/chart2/DataPointLabel.hpp>
66#include <com/sun/star/chart/ErrorBarStyle.hpp>
67#include <com/sun/star/chart/TimeUnit.hpp>
68#include <com/sun/star/chart2/MovingAverageType.hpp>
69#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
70#include <com/sun/star/container/XChild.hpp>
71#include <com/sun/star/chart2/RelativePosition.hpp>
72#include <o3tl/safeint.hxx>
73#include <tools/color.hxx>
75#include <rtl/ustrbuf.hxx>
76#include <rtl/math.hxx>
78#include <com/sun/star/drawing/LineStyle.hpp>
79#include <com/sun/star/util/XCloneable.hpp>
80
83#include <utility>
84#include <vcl/svapp.hxx>
85#include <vcl/settings.hxx>
87#include <sal/log.hxx>
88
89#include <functional>
90#include <map>
91#include <unordered_map>
92
93
94namespace chart {
95
96using namespace ::com::sun::star;
97using namespace ::com::sun::star::chart;
98using namespace ::com::sun::star::chart2;
99using namespace ::chart::DataSeriesProperties;
100using ::com::sun::star::uno::Reference;
101using ::com::sun::star::uno::Sequence;
102
104 : m_bValuesDirty(true)
105 , m_fMinimumY(0.0)
106 , m_fMaximumY(0.0)
107{
108}
109
110VDataSeriesGroup::VDataSeriesGroup( std::unique_ptr<VDataSeries> pSeries )
111 : m_aSeriesVector(1)
114{
115 m_aSeriesVector[0] = std::move(pSeries);
116}
117
119 : m_aSeriesVector( std::move(other.m_aSeriesVector) )
120 , m_bMaxPointCountDirty( other.m_bMaxPointCountDirty )
121 , m_nMaxPointCount( other.m_nMaxPointCount )
122 , m_aListOfCachedYValues( std::move(other.m_aListOfCachedYValues) )
123{
124}
125
127{
128}
129
131{
132 //delete all data series help objects:
133 m_aSeriesVector.clear();
134}
135
136void VDataSeriesGroup::addSeries( std::unique_ptr<VDataSeries> pSeries )
137{
138 m_aSeriesVector.push_back(std::move(pSeries));
140}
141
143{
144 return m_aSeriesVector.size();
145}
146
148 , sal_Int32 nDimensionCount, bool bCategoryXAxis )
149 : PlotterBase( nDimensionCount )
150 , m_pMainPosHelper( nullptr )
151 , m_xChartTypeModel(std::move(xChartTypeModel))
152 , m_bCategoryXAxis(bCategoryXAxis)
153 , m_nTimeResolution(css::chart::TimeUnit::DAY)
154 , m_aNullDate(30,12,1899)
155 , m_pExplicitCategoriesProvider(nullptr)
156 , m_bPointsWereSkipped(false)
157 , m_bPieLabelsAllowToMove(false)
158 , m_aAvailableOuterRect(0, 0, 0, 0)
159{
160 SAL_WARN_IF(!m_xChartTypeModel.is(),"chart2","no XChartType available in view, fallback to default values may be wrong");
161}
162
164{
165 //delete all data series help objects:
166 for (std::vector<VDataSeriesGroup> & rGroupVector : m_aZSlots)
167 {
168 for (VDataSeriesGroup & rGroup : rGroupVector)
169 {
170 rGroup.deleteSeries();
171 }
172 rGroupVector.clear();
173 }
174 m_aZSlots.clear();
175
177
179}
180
181void VSeriesPlotter::addSeries( std::unique_ptr<VDataSeries> pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot )
182{
183 //take ownership of pSeries
184
185 OSL_PRECOND( pSeries, "series to add is NULL" );
186 if(!pSeries)
187 return;
188
190 {
193 else
194 pSeries->setCategoryXAxis();
195 }
196 else
197 {
199 pSeries->setXValuesIfNone( m_pExplicitCategoriesProvider->getOriginalCategories() );
200 }
201
202 if(zSlot<0 || o3tl::make_unsigned(zSlot)>=m_aZSlots.size())
203 {
204 //new z slot
205 std::vector< VDataSeriesGroup > aZSlot;
206 aZSlot.emplace_back( std::move(pSeries) );
207 m_aZSlots.push_back( std::move(aZSlot) );
208 }
209 else
210 {
211 //existing zslot
212 std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[zSlot];
213
214 if(xSlot<0 || o3tl::make_unsigned(xSlot)>=rXSlots.size())
215 {
216 //append the series to already existing x series
217 rXSlots.emplace_back( std::move(pSeries) );
218 }
219 else
220 {
221 //x slot is already occupied
222 //y slot decides what to do:
223
224 VDataSeriesGroup& rYSlots = rXSlots[xSlot];
225 sal_Int32 nYSlotCount = rYSlots.getSeriesCount();
226
227 if( ySlot < -1 )
228 {
229 //move all existing series in the xSlot to next slot
230 //@todo
231 OSL_FAIL( "Not implemented yet");
232 }
233 else if( ySlot == -1 || ySlot >= nYSlotCount)
234 {
235 //append the series to already existing y series
236 rYSlots.addSeries( std::move(pSeries) );
237 }
238 else
239 {
240 //y slot is already occupied
241 //insert at given y and x position
242
243 //@todo
244 OSL_FAIL( "Not implemented yet");
245 }
246 }
247 }
248}
249
251{
252 drawing::Direction3D aRet(1.0,1.0,1.0);
253 if (!m_pPosHelper)
254 return aRet;
255
256 drawing::Direction3D aScale( m_pPosHelper->getScaledLogicWidth() );
257 aRet.DirectionZ = aScale.DirectionZ*0.2;
258 if(aRet.DirectionZ>1.0)
259 aRet.DirectionZ=1.0;
260 if(aRet.DirectionZ>10)
261 aRet.DirectionZ=10;
262 return aRet;
263}
264
266{
267 for (std::vector<VDataSeriesGroup> const & rGroupVector : m_aZSlots)
268 {
269 for (VDataSeriesGroup const & rGroup : rGroupVector)
270 {
271 //iterate through all series in this x slot
272 for (std::unique_ptr<VDataSeries> const & pSeries : rGroup.m_aSeriesVector)
273 {
274 pSeries->releaseShapes();
275 }
276 }
277 }
278}
279
281 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget )
282{
283 if( !pDataSeries->m_xGroupShape )
284 //create a group shape for this series and add to logic target:
285 pDataSeries->m_xGroupShape = createGroupShape( xTarget,pDataSeries->getCID() );
286 return pDataSeries->m_xGroupShape;
287}
288
290 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget )
291{
292 if(!pDataSeries->m_xFrontSubGroupShape)
293 {
294 //ensure that the series group shape is already created
295 rtl::Reference<SvxShapeGroupAnyD> xSeriesShapes( getSeriesGroupShape( pDataSeries, xTarget ) );
296 //ensure that the back child is created first
298 //use series group shape as parent for the new created front group shape
299 pDataSeries->m_xFrontSubGroupShape = createGroupShape( xSeriesShapes );
300 }
301 return pDataSeries->m_xFrontSubGroupShape;
302}
303
305 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget )
306{
307 if(!pDataSeries->m_xBackSubGroupShape)
308 {
309 //ensure that the series group shape is already created
310 rtl::Reference<SvxShapeGroupAnyD> xSeriesShapes( getSeriesGroupShape( pDataSeries, xTarget ) );
311 //use series group shape as parent for the new created back group shape
312 pDataSeries->m_xBackSubGroupShape = createGroupShape( xSeriesShapes );
313 }
314 return pDataSeries->m_xBackSubGroupShape;
315}
316
318 , const rtl::Reference<SvxShapeGroupAnyD>& xTextTarget )
319{
320 //xTextTarget needs to be a 2D shape container always!
321 if(!rDataSeries.m_xLabelsGroupShape)
322 {
323 //create a 2D group shape for texts of this series and add to text target:
324 rDataSeries.m_xLabelsGroupShape = ShapeFactory::createGroup2D( xTextTarget, rDataSeries.getLabelsCID() );
325 }
326 return rDataSeries.m_xLabelsGroupShape;
327}
328
330 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
331 , bool bYError )
332{
334 bYError ? rDataSeries.m_xErrorYBarsGroupShape : rDataSeries.m_xErrorXBarsGroupShape;
335
336 if(!rShapeGroup)
337 {
338 //create a group shape for this series and add to logic target:
339 rShapeGroup = createGroupShape( xTarget,rDataSeries.getErrorBarsCID(bYError) );
340 }
341 return rShapeGroup;
342
343}
344
346 , sal_Int32 nPointIndex
347 , double fValue
348 , bool bAsPercentage )
349{
350 OUString aNumber;
351
353 {
354 sal_Int32 nNumberFormatKey = 0;
355 if( rDataSeries.hasExplicitNumberFormat(nPointIndex,bAsPercentage) )
356 nNumberFormatKey = rDataSeries.getExplicitNumberFormat(nPointIndex,bAsPercentage);
357 else if( bAsPercentage )
358 {
359 sal_Int32 nPercentFormat = DiagramHelper::getPercentNumberFormat( m_apNumberFormatterWrapper->getNumberFormatsSupplier() );
360 if( nPercentFormat != -1 )
361 nNumberFormatKey = nPercentFormat;
362 }
363 else
364 {
365 nNumberFormatKey = rDataSeries.detectNumberFormatKey( nPointIndex );
366 }
367 if(nNumberFormatKey<0)
368 nNumberFormatKey=0;
369
370 Color nLabelCol;
371 bool bColChanged;
372 aNumber = m_apNumberFormatterWrapper->getFormattedString(
373 nNumberFormatKey, fValue, nLabelCol, bColChanged );
374 //@todo: change color of label if bColChanged is true
375 }
376 else
377 {
379 const OUString& aNumDecimalSep = rLocaleDataWrapper.getNumDecimalSep();
380 assert(aNumDecimalSep.getLength() > 0);
381 sal_Unicode cDecSeparator = aNumDecimalSep[0];
382 aNumber = ::rtl::math::doubleToUString( fValue, rtl_math_StringFormat_G /*rtl_math_StringFormat*/
383 , 3/*DecPlaces*/ , cDecSeparator );
384 }
385 return aNumber;
386}
387
389 , VDataSeries& rDataSeries
390 , sal_Int32 nPointIndex
391 , double fValue
392 , double fSumValue
393 , const awt::Point& rScreenPosition2D
394 , LabelAlignment eAlignment
395 , sal_Int32 nOffset
396 , sal_Int32 nTextWidth )
397{
400
401 try
402 {
404 rDataSeries.getPropertiesOfPoint( nPointIndex ) );
405 if( xPropertySet.is() )
406 {
407 uno::Any aAny = xPropertySet->getPropertyValue( CHART_UNONAME_CUSTOM_LABEL_FIELDS );
408 if( aAny.hasValue() )
409 {
410 aAny >>= aCustomLabels;
411 }
412 }
413
414 awt::Point aScreenPosition2D(rScreenPosition2D);
415 if(eAlignment==LABEL_ALIGN_LEFT)
416 aScreenPosition2D.X -= nOffset;
417 else if(eAlignment==LABEL_ALIGN_RIGHT)
418 aScreenPosition2D.X += nOffset;
419 else if(eAlignment==LABEL_ALIGN_TOP)
420 aScreenPosition2D.Y -= nOffset;
421 else if(eAlignment==LABEL_ALIGN_BOTTOM)
422 aScreenPosition2D.Y += nOffset;
423
426 getLabelsGroupShape(rDataSeries, xTarget),
427 ObjectIdentifier::createPointCID( rDataSeries.getLabelCID_Stub(), nPointIndex));
428
429 //check whether the label needs to be created and how:
430 DataPointLabel* pLabel = rDataSeries.getDataPointLabelIfLabel( nPointIndex );
431
432 if( !pLabel )
433 return xTextShape;
434
435 //prepare legend symbol
436
437 // get the font size for the label through the "CharHeight" property
438 // attached to the passed data series entry.
439 // (By tracing font height values it results that for pie chart the
440 // font size is not the same for all labels, but here no font size
441 // modification occurs).
442 float fViewFontSize( 10.0 );
443 {
444 uno::Reference< beans::XPropertySet > xProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) );
445 if( xProps.is() )
446 xProps->getPropertyValue( "CharHeight") >>= fViewFontSize;
447 fViewFontSize = convertPointToMm100(fViewFontSize);
448 }
449
450 // the font height is used for computing the size of an optional legend
451 // symbol to be prepended to the text label.
453 if(pLabel->ShowLegendSymbol)
454 {
455 sal_Int32 nSymbolHeight = static_cast< sal_Int32 >( fViewFontSize * 0.6 );
456 awt::Size aCurrentRatio = getPreferredLegendKeyAspectRatio();
457 sal_Int32 nSymbolWidth = aCurrentRatio.Width;
458 if( aCurrentRatio.Height > 0 )
459 {
460 nSymbolWidth = nSymbolHeight* aCurrentRatio.Width/aCurrentRatio.Height;
461 }
462 awt::Size aMaxSymbolExtent( nSymbolWidth, nSymbolHeight );
463
464 if( rDataSeries.isVaryColorsByPoint() )
465 xSymbol = VSeriesPlotter::createLegendSymbolForPoint( aMaxSymbolExtent, rDataSeries, nPointIndex, xTarget_ );
466 else
467 xSymbol = VSeriesPlotter::createLegendSymbolForSeries( aMaxSymbolExtent, rDataSeries, xTarget_ );
468 }
469
470 //prepare text
471 bool bTextWrap = false;
472 OUString aSeparator(" ");
473 double fRotationDegrees = 0.0;
474 try
475 {
476 uno::Reference< beans::XPropertySet > xPointProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) );
477 if(xPointProps.is())
478 {
479 xPointProps->getPropertyValue( "TextWordWrap" ) >>= bTextWrap;
480 xPointProps->getPropertyValue( "LabelSeparator" ) >>= aSeparator;
481 // Extract the optional text rotation through the
482 // "TextRotation" property attached to the passed data point.
483 xPointProps->getPropertyValue( "TextRotation" ) >>= fRotationDegrees;
484 }
485 }
486 catch( const uno::Exception& )
487 {
488 TOOLS_WARN_EXCEPTION("chart2", "" );
489 }
490
491 sal_Int32 nLineCountForSymbolsize = 0;
492 sal_uInt32 nTextListLength = 4;
493 sal_uInt32 nCustomLabelsCount = aCustomLabels.getLength();
494 Sequence< OUString > aTextList( nTextListLength );
495
496 bool bUseCustomLabel = nCustomLabelsCount > 0;
497 if( bUseCustomLabel )
498 {
499 nTextListLength = ( nCustomLabelsCount > 3 ) ? nCustomLabelsCount : 3;
500 aSeparator = "";
501 aTextList = Sequence< OUString >( nTextListLength );
502 auto pTextList = aTextList.getArray();
503 for( sal_uInt32 i = 0; i < nCustomLabelsCount; ++i )
504 {
505 switch( aCustomLabels[i]->getFieldType() )
506 {
507 case DataPointCustomLabelFieldType_VALUE:
508 {
509 pTextList[i] = getLabelTextForValue( rDataSeries, nPointIndex, fValue, false );
510 break;
511 }
512 case DataPointCustomLabelFieldType_CATEGORYNAME:
513 {
514 pTextList[i] = getCategoryName( nPointIndex );
515 break;
516 }
517 case DataPointCustomLabelFieldType_SERIESNAME:
518 {
519 OUString aRole;
520 if ( m_xChartTypeModel )
521 aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel();
522 const rtl::Reference< DataSeries >& xSeries( rDataSeries.getModel() );
523 pTextList[i] = xSeries->getLabelForRole( aRole );
524 break;
525 }
526 case DataPointCustomLabelFieldType_PERCENTAGE:
527 {
528 if(fSumValue == 0.0)
529 fSumValue = 1.0;
530 fValue /= fSumValue;
531 if(fValue < 0)
532 fValue *= -1.0;
533
534 pTextList[i] = getLabelTextForValue(rDataSeries, nPointIndex, fValue, true);
535 break;
536 }
537 case DataPointCustomLabelFieldType_CELLRANGE:
538 {
539 if (aCustomLabels[i]->getDataLabelsRange())
540 pTextList[i] = aCustomLabels[i]->getString();
541 else
542 pTextList[i] = OUString();
543 break;
544 }
545 case DataPointCustomLabelFieldType_CELLREF:
546 {
547 // TODO: for now doesn't show placeholder
548 pTextList[i] = OUString();
549 break;
550 }
551 case DataPointCustomLabelFieldType_TEXT:
552 {
553 pTextList[i] = aCustomLabels[i]->getString();
554 break;
555 }
556 case DataPointCustomLabelFieldType_NEWLINE:
557 {
558 pTextList[i] = "\n";
559 break;
560 }
561 default:
562 break;
563 }
564 aCustomLabels[i]->setString( aTextList[i] );
565 }
566 }
567 else
568 {
569 auto pTextList = aTextList.getArray();
570 if( pLabel->ShowCategoryName )
571 {
572 pTextList[0] = getCategoryName( nPointIndex );
573 }
574
575 if( pLabel->ShowSeriesName )
576 {
577 OUString aRole;
578 if ( m_xChartTypeModel )
579 aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel();
580 const rtl::Reference< DataSeries >& xSeries( rDataSeries.getModel() );
581 pTextList[1] = xSeries->getLabelForRole( aRole );
582 }
583
584 if( pLabel->ShowNumber )
585 {
586 pTextList[2] = getLabelTextForValue(rDataSeries, nPointIndex, fValue, false);
587 }
588
589 if( pLabel->ShowNumberInPercent )
590 {
591 if(fSumValue==0.0)
592 fSumValue=1.0;
593 fValue /= fSumValue;
594 if( fValue < 0 )
595 fValue*=-1.0;
596
597 pTextList[3] = getLabelTextForValue(rDataSeries, nPointIndex, fValue, true);
598 }
599 }
600
601 for( auto const & line : std::as_const(aTextList) )
602 {
603 if( !line.isEmpty() )
604 {
605 ++nLineCountForSymbolsize;
606 }
607 }
608
609 //prepare properties for multipropertyset-interface of shape
610 tNameSequence* pPropNames;
611 tAnySequence* pPropValues;
612 if( !rDataSeries.getTextLabelMultiPropertyLists( nPointIndex, pPropNames, pPropValues ) )
613 return xTextShape;
614
615 // set text alignment for the text shape to be created.
616 LabelPositionHelper::changeTextAdjustment( *pPropValues, *pPropNames, eAlignment );
617
618 // check if data series entry percent value and absolute value have to
619 // be appended to the text label, and what should be the separator
620 // character (comma, space, new line). In case it is a new line we get
621 // a multi-line label.
622 bool bMultiLineLabel = ( aSeparator == "\n" );
623
624 if( bUseCustomLabel )
625 {
628
629 // create text shape
630 xTextShape = ShapeFactory::
631 createText( xTarget_, aFormattedLabels, *pPropNames, *pPropValues,
632 ShapeFactory::makeTransformation( aScreenPosition2D ) );
633 }
634 else
635 {
636 // join text list elements
637 OUStringBuffer aText;
638 for( sal_uInt32 nN = 0; nN < nTextListLength; ++nN)
639 {
640 if( !aTextList[nN].isEmpty() )
641 {
642 if( !aText.isEmpty() )
643 {
644 aText.append(aSeparator);
645 }
646 aText.append( aTextList[nN] );
647 }
648 }
649
650 //create text shape
651 xTextShape = ShapeFactory::
652 createText( xTarget_, aText.makeStringAndClear(), *pPropNames, *pPropValues,
653 ShapeFactory::makeTransformation( aScreenPosition2D ) );
654 }
655
656 if( !xTextShape.is() )
657 return xTextShape;
658
659 // we need to use a default value for the maximum width property ?
660 if( nTextWidth == 0 && bTextWrap )
661 {
662 sal_Int32 nMinSize =
664 ? m_aPageReferenceSize.Height
665 : m_aPageReferenceSize.Width;
666 nTextWidth = nMinSize / 3;
667 }
668
669 // in case text must be wrapped set the maximum width property
670 // for the text shape
671 if( nTextWidth != 0 && bTextWrap )
672 {
673 // compute the height of a line of text
674 if( !bMultiLineLabel || nLineCountForSymbolsize <= 0 )
675 {
676 nLineCountForSymbolsize = 1;
677 }
678 awt::Size aTextSize = xTextShape->getSize();
679 sal_Int32 aTextLineHeight = aTextSize.Height / nLineCountForSymbolsize;
680
681 // set maximum text width
682 uno::Any aTextMaximumFrameWidth( nTextWidth );
683 xTextShape->SvxShape::setPropertyValue( "TextMaximumFrameWidth", aTextMaximumFrameWidth );
684
685 // compute the total lines of text
686 aTextSize = xTextShape->getSize();
687 nLineCountForSymbolsize = aTextSize.Height / aTextLineHeight;
688 }
689
690 // in case text is rotated, the transformation property of the text
691 // shape is modified.
692 if( fRotationDegrees != 0.0 )
693 {
694 const double fDegreesPi( -basegfx::deg2rad(fRotationDegrees) );
695 xTextShape->SvxShape::setPropertyValue( "Transformation", ShapeFactory::makeTransformation( aScreenPosition2D, fDegreesPi ) );
696 LabelPositionHelper::correctPositionForRotation( xTextShape, eAlignment, fRotationDegrees, true /*bRotateAroundCenter*/ );
697 }
698
699 awt::Point aTextShapePos(xTextShape->getPosition());
700 if( m_bPieLabelsAllowToMove && rDataSeries.isLabelCustomPos(nPointIndex) )
701 {
702 awt::Point aRelPos = rDataSeries.getLabelPosition(aTextShapePos, nPointIndex);
703 if( aRelPos.X != -1 )
704 {
705 xTextShape->setPosition(aRelPos);
706 if( !m_xChartTypeModel->getChartType().equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE) &&
707 // "ShowCustomLeaderLines"
708 rDataSeries.getModel()->getFastPropertyValue( PROP_DATASERIES_SHOW_CUSTOM_LEADERLINES ).get<sal_Bool>())
709 {
710 sal_Int32 nX1 = rScreenPosition2D.X;
711 sal_Int32 nY1 = rScreenPosition2D.Y;
712 sal_Int32 nX2 = nX1;
713 sal_Int32 nY2 = nY1;
714 ::basegfx::B2IRectangle aRect(BaseGFXHelper::makeRectangle(aRelPos, xTextShape->getSize()));
715 if (nX1 < aRelPos.X)
716 nX2 = aRelPos.X;
717 else if (nX1 > aRect.getMaxX())
718 nX2 = aRect.getMaxX();
719
720 if (nY1 < aRect.getMinY())
721 nY2 = aRect.getMinY();
722 else if (nY1 > aRect.getMaxY())
723 nY2 = aRect.getMaxY();
724
725 //when the line is very short compared to the page size don't create one
726 ::basegfx::B2DVector aLength(nX1 - nX2, nY1 - nY2);
727 double fPageDiagonaleLength
728 = std::hypot(m_aPageReferenceSize.Width, m_aPageReferenceSize.Height);
729 if ((aLength.getLength() / fPageDiagonaleLength) >= 0.01)
730 {
731 drawing::PointSequenceSequence aPoints{ { {nX1, nY1}, {nX2, nY2} } };
732
733 VLineProperties aVLineProperties;
734 ShapeFactory::createLine2D(xTarget, aPoints, &aVLineProperties);
735 }
736 }
737 }
738 }
739
740 // in case legend symbol has to be displayed, text shape position is
741 // slightly changed.
742 const awt::Point aUnrotatedTextPos(xTextShape->getPosition());
743 if( xSymbol.is() )
744 {
745 const awt::Point aOldTextPos( xTextShape->getPosition() );
746 awt::Point aNewTextPos( aOldTextPos );
747
748 awt::Point aSymbolPosition( aUnrotatedTextPos );
749 awt::Size aSymbolSize( xSymbol->getSize() );
750 awt::Size aTextSize = xTextShape->getSize();
751
752 sal_Int32 nXDiff = aSymbolSize.Width + static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.22 ) );//minimum 1mm
753 if( !bMultiLineLabel || nLineCountForSymbolsize <= 0 )
754 nLineCountForSymbolsize = 1;
755 aSymbolPosition.Y += ((aTextSize.Height/nLineCountForSymbolsize)/4);
756
757 if(eAlignment==LABEL_ALIGN_LEFT
758 || eAlignment==LABEL_ALIGN_LEFT_TOP
759 || eAlignment==LABEL_ALIGN_LEFT_BOTTOM)
760 {
761 aSymbolPosition.X -= nXDiff;
762 }
763 else if(eAlignment==LABEL_ALIGN_RIGHT
764 || eAlignment==LABEL_ALIGN_RIGHT_TOP
765 || eAlignment==LABEL_ALIGN_RIGHT_BOTTOM )
766 {
767 aNewTextPos.X += nXDiff;
768 }
769 else if(eAlignment==LABEL_ALIGN_TOP
770 || eAlignment==LABEL_ALIGN_BOTTOM
771 || eAlignment==LABEL_ALIGN_CENTER )
772 {
773 aSymbolPosition.X -= nXDiff/2;
774 aNewTextPos.X += nXDiff/2;
775 }
776
777 xSymbol->setPosition( aSymbolPosition );
778 xTextShape->setPosition( aNewTextPos );
779 }
780 }
781 catch( const uno::Exception& )
782 {
783 TOOLS_WARN_EXCEPTION("chart2", "" );
784 }
785
786 return xTextShape;
787}
788
789namespace
790{
791double lcl_getErrorBarLogicLength(
792 const uno::Sequence< double > & rData,
794 sal_Int32 nErrorBarStyle,
795 sal_Int32 nIndex,
796 bool bPositive,
797 bool bYError )
798{
799 double fResult = std::numeric_limits<double>::quiet_NaN();
800 try
801 {
802 switch( nErrorBarStyle )
803 {
804 case css::chart::ErrorBarStyle::NONE:
805 break;
806 case css::chart::ErrorBarStyle::VARIANCE:
807 fResult = StatisticsHelper::getVariance( rData );
808 break;
809 case css::chart::ErrorBarStyle::STANDARD_DEVIATION:
811 break;
812 case css::chart::ErrorBarStyle::RELATIVE:
813 {
814 double fPercent = 0;
815 if( xProp->getPropertyValue( bPositive
816 ? OUString("PositiveError")
817 : OUString("NegativeError") ) >>= fPercent )
818 {
819 if( nIndex >=0 && nIndex < rData.getLength() &&
820 ! std::isnan( rData[nIndex] ) &&
821 ! std::isnan( fPercent ))
822 {
823 fResult = rData[nIndex] * fPercent / 100.0;
824 }
825 }
826 }
827 break;
828 case css::chart::ErrorBarStyle::ABSOLUTE:
829 xProp->getPropertyValue( bPositive
830 ? OUString("PositiveError")
831 : OUString("NegativeError") ) >>= fResult;
832 break;
833 case css::chart::ErrorBarStyle::ERROR_MARGIN:
834 {
835 // todo: check if this is really what's called error-margin
836 double fPercent = 0;
837 if( xProp->getPropertyValue( bPositive
838 ? OUString("PositiveError")
839 : OUString("NegativeError") ) >>= fPercent )
840 {
841 double fMaxValue = -std::numeric_limits<double>::infinity();
842 for(double d : rData)
843 {
844 if(fMaxValue < d)
845 fMaxValue = d;
846 }
847 if( std::isfinite( fMaxValue ) &&
848 std::isfinite( fPercent ))
849 {
850 fResult = fMaxValue * fPercent / 100.0;
851 }
852 }
853 }
854 break;
855 case css::chart::ErrorBarStyle::STANDARD_ERROR:
856 fResult = StatisticsHelper::getStandardError( rData );
857 break;
858 case css::chart::ErrorBarStyle::FROM_DATA:
859 {
860 uno::Reference< chart2::data::XDataSource > xErrorBarData( xProp, uno::UNO_QUERY );
861 if( xErrorBarData.is())
863 xErrorBarData, nIndex, bPositive, bYError);
864 }
865 break;
866 }
867 }
868 catch( const uno::Exception & )
869 {
870 TOOLS_WARN_EXCEPTION("chart2", "" );
871 }
872
873 return fResult;
874}
875
876void lcl_AddErrorBottomLine( const drawing::Position3D& rPosition, ::basegfx::B2DVector aMainDirection
877 , std::vector<std::vector<css::drawing::Position3D>>& rPoly, sal_Int32 nSequenceIndex )
878{
879 double fFixedWidth = 200.0;
880
881 aMainDirection.normalize();
882 ::basegfx::B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX());
883 aOrthoDirection.normalize();
884
885 ::basegfx::B2DVector aAnchor( rPosition.PositionX, rPosition.PositionY );
886 ::basegfx::B2DVector aStart = aAnchor + aOrthoDirection*fFixedWidth/2.0;
887 ::basegfx::B2DVector aEnd = aAnchor - aOrthoDirection*fFixedWidth/2.0;
888
889 AddPointToPoly( rPoly, drawing::Position3D( aStart.getX(), aStart.getY(), rPosition.PositionZ), nSequenceIndex );
890 AddPointToPoly( rPoly, drawing::Position3D( aEnd.getX(), aEnd.getY(), rPosition.PositionZ), nSequenceIndex );
891}
892
893::basegfx::B2DVector lcl_getErrorBarMainDirection(
894 const drawing::Position3D& rStart
895 , const drawing::Position3D& rBottomEnd
896 , PlottingPositionHelper const * pPosHelper
897 , const drawing::Position3D& rUnscaledLogicPosition
898 , bool bYError )
899{
900 ::basegfx::B2DVector aMainDirection( rStart.PositionX - rBottomEnd.PositionX
901 , rStart.PositionY - rBottomEnd.PositionY );
902 if( !aMainDirection.getLength() )
903 {
904 //get logic clip values:
905 double MinX = pPosHelper->getLogicMinX();
906 double MinY = pPosHelper->getLogicMinY();
907 double MaxX = pPosHelper->getLogicMaxX();
908 double MaxY = pPosHelper->getLogicMaxY();
909 double fZ = pPosHelper->getLogicMinZ();
910
911 if( bYError )
912 {
913 //main direction has constant x value
914 MinX = rUnscaledLogicPosition.PositionX;
915 MaxX = rUnscaledLogicPosition.PositionX;
916 }
917 else
918 {
919 //main direction has constant y value
920 MinY = rUnscaledLogicPosition.PositionY;
921 MaxY = rUnscaledLogicPosition.PositionY;
922 }
923
924 drawing::Position3D aStart = pPosHelper->transformLogicToScene( MinX, MinY, fZ, false );
925 drawing::Position3D aEnd = pPosHelper->transformLogicToScene( MaxX, MaxY, fZ, false );
926
927 aMainDirection = ::basegfx::B2DVector( aStart.PositionX - aEnd.PositionX
928 , aStart.PositionY - aEnd.PositionY );
929 }
930 if( !aMainDirection.getLength() )
931 {
932 //@todo
933 }
934 return aMainDirection;
935}
936
937drawing::Position3D lcl_transformMixedToScene( PlottingPositionHelper const * pPosHelper
938 , double fX /*scaled*/, double fY /*unscaled*/, double fZ /*unscaled*/ )
939{
940 if(!pPosHelper)
941 return drawing::Position3D(0,0,0);
942 pPosHelper->doLogicScaling( nullptr,&fY,&fZ );
943 pPosHelper->clipScaledLogicValues( &fX,&fY,&fZ );
944 return pPosHelper->transformScaledLogicToScene( fX, fY, fZ, false );
945}
946
947} // anonymous namespace
948
951 , const drawing::Position3D& rUnscaledLogicPosition
952 , const uno::Reference< beans::XPropertySet > & xErrorBarProperties
953 , const VDataSeries& rVDataSeries
954 , sal_Int32 nIndex
955 , bool bYError /* = true */
956 , const double* pfScaledLogicX
957 )
958{
960 return;
961
962 if( ! xErrorBarProperties.is())
963 return;
964
965 try
966 {
967 bool bShowPositive = false;
968 bool bShowNegative = false;
969 sal_Int32 nErrorBarStyle = css::chart::ErrorBarStyle::VARIANCE;
970
971 xErrorBarProperties->getPropertyValue( "ShowPositiveError") >>= bShowPositive;
972 xErrorBarProperties->getPropertyValue( "ShowNegativeError") >>= bShowNegative;
973 xErrorBarProperties->getPropertyValue( "ErrorBarStyle") >>= nErrorBarStyle;
974
975 if(!bShowPositive && !bShowNegative)
976 return;
977
978 if(nErrorBarStyle==css::chart::ErrorBarStyle::NONE)
979 return;
980
981 if (!m_pPosHelper)
982 return;
983
984 drawing::Position3D aUnscaledLogicPosition(rUnscaledLogicPosition);
985 if(nErrorBarStyle==css::chart::ErrorBarStyle::STANDARD_DEVIATION)
986 {
987 if (bYError)
988 aUnscaledLogicPosition.PositionY = rVDataSeries.getYMeanValue();
989 else
990 aUnscaledLogicPosition.PositionX = rVDataSeries.getXMeanValue();
991 }
992
993 bool bCreateNegativeBorder = false;//make a vertical line at the negative end of the error bar
994 bool bCreatePositiveBorder = false;//make a vertical line at the positive end of the error bar
995 drawing::Position3D aMiddle(aUnscaledLogicPosition);
996 const double fX = aUnscaledLogicPosition.PositionX;
997 const double fY = aUnscaledLogicPosition.PositionY;
998 const double fZ = aUnscaledLogicPosition.PositionZ;
999 double fScaledX = fX;
1000 if( pfScaledLogicX )
1001 fScaledX = *pfScaledLogicX;
1002 else
1003 m_pPosHelper->doLogicScaling( &fScaledX, nullptr, nullptr );
1004
1005 aMiddle = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fY, fZ );
1006
1007 drawing::Position3D aNegative(aMiddle);
1008 drawing::Position3D aPositive(aMiddle);
1009
1010 uno::Sequence< double > aData( bYError ? rVDataSeries.getAllY() : rVDataSeries.getAllX() );
1011
1012 if( bShowPositive )
1013 {
1014 double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, true, bYError );
1015 if( std::isfinite( fLength ) )
1016 {
1017 double fLocalX = fX;
1018 double fLocalY = fY;
1019 if( bYError )
1020 {
1021 fLocalY+=fLength;
1022 aPositive = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ );
1023 }
1024 else
1025 {
1026 fLocalX+=fLength;
1027 aPositive = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true );
1028 }
1029 bCreatePositiveBorder = m_pPosHelper->isLogicVisible(fLocalX, fLocalY, fZ);
1030 }
1031 else
1032 bShowPositive = false;
1033 }
1034
1035 if( bShowNegative )
1036 {
1037 double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, false, bYError );
1038 if( std::isfinite( fLength ) )
1039 {
1040 double fLocalX = fX;
1041 double fLocalY = fY;
1042 if( bYError )
1043 {
1044 fLocalY-=fLength;
1045 aNegative = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ );
1046 }
1047 else
1048 {
1049 fLocalX-=fLength;
1050 aNegative = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true );
1051 }
1052 if (std::isfinite(aNegative.PositionX) &&
1053 std::isfinite(aNegative.PositionY) &&
1054 std::isfinite(aNegative.PositionZ)) {
1055 bCreateNegativeBorder = m_pPosHelper->isLogicVisible( fLocalX, fLocalY, fZ);
1056 } else {
1057 // If error bars result in a numerical problem (e.g., an
1058 // error bar on a logarithmic chart that results in a point
1059 // <= 0) then just turn off the error bar.
1060 //
1061 // TODO: This perhaps should display a warning, so the user
1062 // knows why a bar is not appearing.
1063 // TODO: This test could also be added to the positive case,
1064 // though a numerical overflow there is less likely.
1065 bShowNegative = false;
1066 }
1067 }
1068 else
1069 bShowNegative = false;
1070 }
1071
1072 if(!bShowPositive && !bShowNegative)
1073 return;
1074
1075 std::vector<std::vector<css::drawing::Position3D>> aPoly;
1076
1077 sal_Int32 nSequenceIndex=0;
1078 if( bShowNegative )
1079 AddPointToPoly( aPoly, aNegative, nSequenceIndex );
1080 AddPointToPoly( aPoly, aMiddle, nSequenceIndex );
1081 if( bShowPositive )
1082 AddPointToPoly( aPoly, aPositive, nSequenceIndex );
1083
1084 if( bShowNegative && bCreateNegativeBorder )
1085 {
1086 ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aNegative, m_pPosHelper, aUnscaledLogicPosition, bYError );
1087 nSequenceIndex++;
1088 lcl_AddErrorBottomLine( aNegative, aMainDirection, aPoly, nSequenceIndex );
1089 }
1090 if( bShowPositive && bCreatePositiveBorder )
1091 {
1092 ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aPositive, m_pPosHelper, aUnscaledLogicPosition, bYError );
1093 nSequenceIndex++;
1094 lcl_AddErrorBottomLine( aPositive, aMainDirection, aPoly, nSequenceIndex );
1095 }
1096
1099 }
1100 catch( const uno::Exception & )
1101 {
1102 TOOLS_WARN_EXCEPTION("chart2", "" );
1103 }
1104
1105}
1106
1108 const drawing::Position3D& rPos0
1109 ,const drawing::Position3D& rPos1
1110 ,const rtl::Reference<SvxShapeGroupAnyD>& rTarget
1111 ,const uno::Reference< beans::XPropertySet >& rErrorBorderProp )
1112{
1113 std::vector<std::vector<css::drawing::Position3D>> aPoly { { rPos0, rPos1} };
1115 rTarget, aPoly );
1116 PropertyMapper::setMappedProperties( *xShape, rErrorBorderProp,
1118}
1119
1121 const drawing::Position3D& rUnscaledLogicPosition
1122 ,VDataSeries& rVDataSeries
1123 ,sal_Int32 nIndex
1124 ,const rtl::Reference<SvxShapeGroupAnyD>& rTarget
1125 ,bool bUseXErrorData
1126 ,bool bUseYErrorData )
1127{
1128 if ( m_nDimension != 2 )
1129 return;
1130
1131 // error border properties
1132 Reference< beans::XPropertySet > xErrorBorderPropX, xErrorBorderPropY;
1133 if ( bUseXErrorData )
1134 {
1135 xErrorBorderPropX = rVDataSeries.getXErrorBarProperties( nIndex );
1136 if ( !xErrorBorderPropX.is() )
1137 return;
1138 }
1139 rtl::Reference<SvxShapeGroupAnyD> xErrorBorder_ShapesX =
1140 getErrorBarsGroupShape( rVDataSeries, rTarget, false );
1141
1142 if ( bUseYErrorData )
1143 {
1144 xErrorBorderPropY = rVDataSeries.getYErrorBarProperties( nIndex );
1145 if ( !xErrorBorderPropY.is() )
1146 return;
1147 }
1148 rtl::Reference<SvxShapeGroupAnyD> xErrorBorder_ShapesY =
1149 getErrorBarsGroupShape( rVDataSeries, rTarget, true );
1150
1152 return;
1153
1154 try
1155 {
1156 bool bShowXPositive = false;
1157 bool bShowXNegative = false;
1158 bool bShowYPositive = false;
1159 bool bShowYNegative = false;
1160
1161 sal_Int32 nErrorBorderStyleX = css::chart::ErrorBarStyle::VARIANCE;
1162 sal_Int32 nErrorBorderStyleY = css::chart::ErrorBarStyle::VARIANCE;
1163
1164 if ( bUseXErrorData )
1165 {
1166 xErrorBorderPropX->getPropertyValue( "ErrorBarStyle" ) >>= nErrorBorderStyleX;
1167 xErrorBorderPropX->getPropertyValue( "ShowPositiveError") >>= bShowXPositive;
1168 xErrorBorderPropX->getPropertyValue( "ShowNegativeError") >>= bShowXNegative;
1169 }
1170 if ( bUseYErrorData )
1171 {
1172 xErrorBorderPropY->getPropertyValue( "ErrorBarStyle" ) >>= nErrorBorderStyleY;
1173 xErrorBorderPropY->getPropertyValue( "ShowPositiveError") >>= bShowYPositive;
1174 xErrorBorderPropY->getPropertyValue( "ShowNegativeError") >>= bShowYNegative;
1175 }
1176
1177 if ( bUseXErrorData && nErrorBorderStyleX == css::chart::ErrorBarStyle::NONE )
1178 bUseXErrorData = false;
1179 if ( bUseYErrorData && nErrorBorderStyleY == css::chart::ErrorBarStyle::NONE )
1180 bUseYErrorData = false;
1181
1182 if ( !bShowXPositive && !bShowXNegative && !bShowYPositive && !bShowYNegative )
1183 return;
1184
1185 if ( !m_pPosHelper )
1186 return;
1187
1188 drawing::Position3D aUnscaledLogicPosition( rUnscaledLogicPosition );
1189 if ( bUseXErrorData && nErrorBorderStyleX == css::chart::ErrorBarStyle::STANDARD_DEVIATION )
1190 aUnscaledLogicPosition.PositionX = rVDataSeries.getXMeanValue();
1191 if ( bUseYErrorData && nErrorBorderStyleY == css::chart::ErrorBarStyle::STANDARD_DEVIATION )
1192 aUnscaledLogicPosition.PositionY = rVDataSeries.getYMeanValue();
1193
1194 const double fX = aUnscaledLogicPosition.PositionX;
1195 const double fY = aUnscaledLogicPosition.PositionY;
1196 const double fZ = aUnscaledLogicPosition.PositionZ;
1197 double fScaledX = fX;
1198 m_pPosHelper->doLogicScaling( &fScaledX, nullptr, nullptr );
1199
1200 uno::Sequence< double > aDataX( rVDataSeries.getAllX() );
1201 uno::Sequence< double > aDataY( rVDataSeries.getAllY() );
1202
1203 double fPosX = 0.0;
1204 double fPosY = 0.0;
1205 double fNegX = 0.0;
1206 double fNegY = 0.0;
1207 if ( bUseXErrorData )
1208 {
1209 if ( bShowXPositive )
1210 fPosX = lcl_getErrorBarLogicLength( aDataX, xErrorBorderPropX,
1211 nErrorBorderStyleX, nIndex, true, false );
1212 if ( bShowXNegative )
1213 fNegX = lcl_getErrorBarLogicLength( aDataX, xErrorBorderPropX,
1214 nErrorBorderStyleX, nIndex, false, false );
1215 }
1216
1217 if ( bUseYErrorData )
1218 {
1219 if ( bShowYPositive )
1220 fPosY = lcl_getErrorBarLogicLength( aDataY, xErrorBorderPropY,
1221 nErrorBorderStyleY, nIndex, true, true );
1222 if ( bShowYNegative )
1223 fNegY = lcl_getErrorBarLogicLength( aDataY, xErrorBorderPropY,
1224 nErrorBorderStyleY, nIndex, false, true );
1225 }
1226
1227 if ( !( std::isfinite( fPosX ) &&
1228 std::isfinite( fPosY ) &&
1229 std::isfinite( fNegX ) &&
1230 std::isfinite( fNegY ) ) )
1231 return;
1232
1233 drawing::Position3D aBottomLeft( lcl_transformMixedToScene( m_pPosHelper,
1234 fX - fNegX, fY - fNegY, fZ ) );
1235 drawing::Position3D aTopLeft( lcl_transformMixedToScene( m_pPosHelper,
1236 fX - fNegX, fY + fPosY, fZ ) );
1237 drawing::Position3D aTopRight( lcl_transformMixedToScene( m_pPosHelper,
1238 fX + fPosX, fY + fPosY, fZ ) );
1239 drawing::Position3D aBottomRight( lcl_transformMixedToScene( m_pPosHelper,
1240 fX + fPosX, fY - fNegY, fZ ) );
1241 if ( bUseXErrorData )
1242 {
1243 // top border
1244 addErrorBorder( aTopLeft, aTopRight, xErrorBorder_ShapesX, xErrorBorderPropX );
1245
1246 // bottom border
1247 addErrorBorder( aBottomRight, aBottomLeft, xErrorBorder_ShapesX, xErrorBorderPropX );
1248 }
1249
1250 if ( bUseYErrorData )
1251 {
1252 // left border
1253 addErrorBorder( aBottomLeft, aTopLeft, xErrorBorder_ShapesY, xErrorBorderPropY );
1254
1255 // right border
1256 addErrorBorder( aTopRight, aBottomRight, xErrorBorder_ShapesY, xErrorBorderPropY );
1257 }
1258 }
1259 catch( const uno::Exception & )
1260 {
1261 DBG_UNHANDLED_EXCEPTION("chart2", "Exception in createErrorRectangle(). ");
1262 }
1263}
1264
1265void VSeriesPlotter::createErrorBar_X( const drawing::Position3D& rUnscaledLogicPosition
1266 , VDataSeries& rVDataSeries, sal_Int32 nPointIndex
1267 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget )
1268{
1269 if(m_nDimension!=2)
1270 return;
1271 // error bars
1272 uno::Reference< beans::XPropertySet > xErrorBarProp(rVDataSeries.getXErrorBarProperties(nPointIndex));
1273 if( xErrorBarProp.is())
1274 {
1275 rtl::Reference<SvxShapeGroupAnyD> xErrorBarsGroup_Shapes =
1276 getErrorBarsGroupShape(rVDataSeries, xTarget, false);
1277
1278 createErrorBar( xErrorBarsGroup_Shapes
1279 , rUnscaledLogicPosition, xErrorBarProp
1280 , rVDataSeries, nPointIndex
1281 , false /* bYError */
1282 , nullptr );
1283 }
1284}
1285
1286void VSeriesPlotter::createErrorBar_Y( const drawing::Position3D& rUnscaledLogicPosition
1287 , VDataSeries& rVDataSeries, sal_Int32 nPointIndex
1288 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
1289 , double const * pfScaledLogicX )
1290{
1291 if(m_nDimension!=2)
1292 return;
1293 // error bars
1294 uno::Reference< beans::XPropertySet > xErrorBarProp(rVDataSeries.getYErrorBarProperties(nPointIndex));
1295 if( xErrorBarProp.is())
1296 {
1297 rtl::Reference<SvxShapeGroupAnyD> xErrorBarsGroup_Shapes =
1298 getErrorBarsGroupShape(rVDataSeries, xTarget, true);
1299
1300 createErrorBar( xErrorBarsGroup_Shapes
1301 , rUnscaledLogicPosition, xErrorBarProp
1302 , rVDataSeries, nPointIndex
1303 , true /* bYError */
1304 , pfScaledLogicX );
1305 }
1306}
1307
1309 const rtl::Reference<SvxShapeGroupAnyD>& xTarget,
1310 const rtl::Reference<SvxShapeGroupAnyD>& xEquationTarget,
1311 bool bMaySkipPoints )
1312{
1313 if(m_nDimension!=2)
1314 return;
1315 rtl::Reference< DataSeries > xContainer( rVDataSeries.getModel() );
1316 if(!xContainer.is())
1317 return;
1318
1319 if (!m_pPosHelper)
1320 return;
1321
1322 const std::vector< rtl::Reference< ::chart::RegressionCurveModel > > & aCurveList = xContainer->getRegressionCurves2();
1323
1324 for(std::size_t nN=0; nN<aCurveList.size(); nN++)
1325 {
1326 const auto & rCurve = aCurveList[nN];
1327 uno::Reference< XRegressionCurveCalculator > xCalculator( rCurve->getCalculator() );
1328 if( !xCalculator.is())
1329 continue;
1330
1331 bool bAverageLine = RegressionCurveHelper::isMeanValueLine( rCurve );
1332
1333 sal_Int32 aDegree = 2;
1334 sal_Int32 aPeriod = 2;
1335 sal_Int32 aMovingAverageType = css::chart2::MovingAverageType::Prior;
1336 double aExtrapolateForward = 0.0;
1337 double aExtrapolateBackward = 0.0;
1338 bool bForceIntercept = false;
1339 double aInterceptValue = 0.0;
1340
1341 if ( !bAverageLine )
1342 {
1343 rCurve->getPropertyValue( "PolynomialDegree") >>= aDegree;
1344 rCurve->getPropertyValue( "MovingAveragePeriod") >>= aPeriod;
1345 rCurve->getPropertyValue( "MovingAverageType") >>= aMovingAverageType;
1346 rCurve->getPropertyValue( "ExtrapolateForward") >>= aExtrapolateForward;
1347 rCurve->getPropertyValue( "ExtrapolateBackward") >>= aExtrapolateBackward;
1348 rCurve->getPropertyValue( "ForceIntercept") >>= bForceIntercept;
1349 if (bForceIntercept)
1350 rCurve->getPropertyValue( "InterceptValue") >>= aInterceptValue;
1351 }
1352
1353 double fChartMinX = m_pPosHelper->getLogicMinX();
1354 double fChartMaxX = m_pPosHelper->getLogicMaxX();
1355
1356 double fMinX = fChartMinX;
1357 double fMaxX = fChartMaxX;
1358
1359 double fPointScale = 1.0;
1360
1361 if( !bAverageLine )
1362 {
1363 rVDataSeries.getMinMaxXValue(fMinX, fMaxX);
1364 fMaxX += aExtrapolateForward;
1365 fMinX -= aExtrapolateBackward;
1366
1367 fPointScale = (fMaxX - fMinX) / (fChartMaxX - fChartMinX);
1368 // sanitize the value, tdf#119922
1369 fPointScale = std::min(fPointScale, 1000.0);
1370 }
1371
1372 xCalculator->setRegressionProperties(aDegree, bForceIntercept, aInterceptValue, aPeriod,
1373 aMovingAverageType);
1374 xCalculator->recalculateRegression(rVDataSeries.getAllX(), rVDataSeries.getAllY());
1375 sal_Int32 nPointCount = 100 * fPointScale;
1376
1377 if ( nPointCount < 2 )
1378 nPointCount = 2;
1379
1380 std::vector< ExplicitScaleData > aScales( m_pPosHelper->getScales());
1383 if( aScales.size() >= 2 )
1384 {
1385 xScalingX.set( aScales[0].Scaling );
1386 xScalingY.set( aScales[1].Scaling );
1387 }
1388
1389 const uno::Sequence< geometry::RealPoint2D > aCalculatedPoints(
1390 xCalculator->getCurveValues(
1391 fMinX, fMaxX, nPointCount,
1392 xScalingX, xScalingY, bMaySkipPoints ));
1393
1394 nPointCount = aCalculatedPoints.getLength();
1395
1396 drawing::PolyPolygonShape3D aRegressionPoly;
1397 aRegressionPoly.SequenceX.realloc(1);
1398 aRegressionPoly.SequenceY.realloc(1);
1399 aRegressionPoly.SequenceZ.realloc(1);
1400 auto pSequenceX = aRegressionPoly.SequenceX.getArray();
1401 auto pSequenceY = aRegressionPoly.SequenceY.getArray();
1402 auto pSequenceZ = aRegressionPoly.SequenceZ.getArray();
1403 pSequenceX[0].realloc(nPointCount);
1404 pSequenceY[0].realloc(nPointCount);
1405 auto pSequenceX0 = pSequenceX[0].getArray();
1406 auto pSequenceY0 = pSequenceY[0].getArray();
1407
1408 sal_Int32 nRealPointCount = 0;
1409
1410 for(geometry::RealPoint2D const & p : aCalculatedPoints)
1411 {
1412 double fLogicX = p.X;
1413 double fLogicY = p.Y;
1414 double fLogicZ = 0.0; //dummy
1415
1416 // fdo#51656: don't scale mean value lines
1417 if(!bAverageLine)
1418 m_pPosHelper->doLogicScaling( &fLogicX, &fLogicY, &fLogicZ );
1419
1420 if(!std::isnan(fLogicX) && !std::isinf(fLogicX) &&
1421 !std::isnan(fLogicY) && !std::isinf(fLogicY) &&
1422 !std::isnan(fLogicZ) && !std::isinf(fLogicZ) )
1423 {
1424 pSequenceX0[nRealPointCount] = fLogicX;
1425 pSequenceY0[nRealPointCount] = fLogicY;
1426 nRealPointCount++;
1427 }
1428 }
1429 pSequenceX[0].realloc(nRealPointCount);
1430 pSequenceY[0].realloc(nRealPointCount);
1431 pSequenceZ[0].realloc(nRealPointCount);
1432
1433 drawing::PolyPolygonShape3D aClippedPoly;
1435 aRegressionPoly = aClippedPoly;
1436 m_pPosHelper->transformScaledLogicToScene( aRegressionPoly );
1437
1438 awt::Point aDefaultPos;
1439 if( aRegressionPoly.SequenceX.hasElements() && aRegressionPoly.SequenceX[0].hasElements() )
1440 {
1441 VLineProperties aVLineProperties;
1442 aVLineProperties.initFromPropertySet( rCurve );
1443
1444 //create an extra group shape for each curve for selection handling
1445 rtl::Reference<SvxShapeGroupAnyD> xRegressionGroupShapes =
1446 createGroupShape( xTarget, rVDataSeries.getDataCurveCID( nN, bAverageLine ) );
1448 xRegressionGroupShapes, PolyToPointSequence( aRegressionPoly ), &aVLineProperties );
1449 ShapeFactory::setShapeName( xShape, "MarkHandles" );
1450 aDefaultPos = xShape->getPosition();
1451 }
1452
1453 // curve equation and correlation coefficient
1454 uno::Reference< beans::XPropertySet > xEquationProperties( rCurve->getEquationProperties());
1455 if( xEquationProperties.is())
1456 {
1458 rVDataSeries.getDataCurveEquationCID( nN ),
1459 xEquationProperties, xEquationTarget, xCalculator,
1460 aDefaultPos );
1461 }
1462 }
1463}
1464
1465static sal_Int32 lcl_getOUStringMaxLineLength ( OUStringBuffer const & aString )
1466{
1467 const sal_Int32 nStringLength = aString.getLength();
1468 sal_Int32 nMaxLineLength = 0;
1469
1470 for ( sal_Int32 i=0; i<nStringLength; i++ )
1471 {
1472 sal_Int32 indexSep = aString.indexOf( "\n", i );
1473 if ( indexSep < 0 )
1474 indexSep = nStringLength;
1475 sal_Int32 nLineLength = indexSep - i;
1476 if ( nLineLength > nMaxLineLength )
1477 nMaxLineLength = nLineLength;
1478 i = indexSep;
1479 }
1480
1481 return nMaxLineLength;
1482}
1483
1485 const OUString & rEquationCID,
1486 const uno::Reference< beans::XPropertySet > & xEquationProperties,
1487 const rtl::Reference<SvxShapeGroupAnyD>& xEquationTarget,
1488 const uno::Reference< chart2::XRegressionCurveCalculator > & xRegressionCurveCalculator,
1489 awt::Point aDefaultPos )
1490{
1491 OSL_ASSERT( xEquationProperties.is());
1492 if( !xEquationProperties.is())
1493 return;
1494
1495 bool bShowEquation = false;
1496 bool bShowCorrCoeff = false;
1497 if(!(( xEquationProperties->getPropertyValue( "ShowEquation") >>= bShowEquation ) &&
1498 ( xEquationProperties->getPropertyValue( "ShowCorrelationCoefficient") >>= bShowCorrCoeff )))
1499 return;
1500
1501 if( ! (bShowEquation || bShowCorrCoeff))
1502 return;
1503
1504 OUStringBuffer aFormula;
1505 sal_Int32 nNumberFormatKey = 0;
1506 sal_Int32 nFormulaWidth = 0;
1507 xEquationProperties->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormatKey;
1508 bool bResizeEquation = true;
1509 sal_Int32 nMaxIteration = 2;
1510 if ( bShowEquation )
1511 {
1512 OUString aXName, aYName;
1513 if ( !(xEquationProperties->getPropertyValue( "XName" ) >>= aXName) )
1514 aXName = OUString( "x" );
1515 if ( !(xEquationProperties->getPropertyValue( "YName" ) >>= aYName) )
1516 aYName = OUString( "f(x)" );
1517 xRegressionCurveCalculator->setXYNames( aXName, aYName );
1518 }
1519
1520 for ( sal_Int32 nCountIteration = 0; bResizeEquation && nCountIteration < nMaxIteration ; nCountIteration++ )
1521 {
1522 bResizeEquation = false;
1523 if( bShowEquation )
1524 {
1526 { // iteration 0: default representation (no wrap)
1527 // iteration 1: expected width (nFormulaWidth) is calculated
1528 aFormula = xRegressionCurveCalculator->getFormattedRepresentation(
1529 m_apNumberFormatterWrapper->getNumberFormatsSupplier(),
1530 nNumberFormatKey, nFormulaWidth );
1531 nFormulaWidth = lcl_getOUStringMaxLineLength( aFormula );
1532 }
1533 else
1534 {
1535 aFormula = xRegressionCurveCalculator->getRepresentation();
1536 }
1537
1538 if( bShowCorrCoeff )
1539 {
1540 aFormula.append( "\n" );
1541 }
1542 }
1543 if( bShowCorrCoeff )
1544 {
1545 aFormula.append( "R" + OUStringChar( aSuperscriptFigures[2] ) + " = " );
1546 double fR( xRegressionCurveCalculator->getCorrelationCoefficient());
1548 {
1549 Color nLabelCol;
1550 bool bColChanged;
1551 aFormula.append(
1552 m_apNumberFormatterWrapper->getFormattedString(
1553 nNumberFormatKey, fR*fR, nLabelCol, bColChanged ));
1554 //@todo: change color of label if bColChanged is true
1555 }
1556 else
1557 {
1558 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
1559 const OUString& aNumDecimalSep = rLocaleDataWrapper.getNumDecimalSep();
1560 sal_Unicode aDecimalSep = aNumDecimalSep[0];
1561 aFormula.append( ::rtl::math::doubleToUString(
1562 fR*fR, rtl_math_StringFormat_G, 4, aDecimalSep, true ));
1563 }
1564 }
1565
1566 awt::Point aScreenPosition2D;
1567 chart2::RelativePosition aRelativePosition;
1568 if( xEquationProperties->getPropertyValue( "RelativePosition") >>= aRelativePosition )
1569 {
1570 //@todo decide whether x is primary or secondary
1571 double fX = aRelativePosition.Primary*m_aPageReferenceSize.Width;
1572 double fY = aRelativePosition.Secondary*m_aPageReferenceSize.Height;
1573 aScreenPosition2D.X = static_cast< sal_Int32 >( ::rtl::math::round( fX ));
1574 aScreenPosition2D.Y = static_cast< sal_Int32 >( ::rtl::math::round( fY ));
1575 }
1576 else
1577 aScreenPosition2D = aDefaultPos;
1578
1579 if( !aFormula.isEmpty())
1580 {
1581 // set fill and line properties on creation
1582 tNameSequence aNames;
1583 tAnySequence aValues;
1584 PropertyMapper::getPreparedTextShapePropertyLists( xEquationProperties, aNames, aValues );
1585
1587 xEquationTarget, aFormula.makeStringAndClear(),
1588 aNames, aValues, ShapeFactory::makeTransformation( aScreenPosition2D ));
1589
1590 ShapeFactory::setShapeName( xTextShape, rEquationCID );
1591 awt::Size aSize( xTextShape->getSize() );
1593 aScreenPosition2D, aSize, aRelativePosition.Anchor ) );
1594 //ensure that the equation is fully placed within the page (if possible)
1595 if( (aPos.X + aSize.Width) > m_aPageReferenceSize.Width )
1596 aPos.X = m_aPageReferenceSize.Width - aSize.Width;
1597 if( aPos.X < 0 )
1598 {
1599 aPos.X = 0;
1600 if ( nFormulaWidth > 0 )
1601 {
1602 bResizeEquation = true;
1603 if ( nCountIteration < nMaxIteration-1 )
1604 xEquationTarget->remove( xTextShape ); // remove equation
1605 nFormulaWidth *= m_aPageReferenceSize.Width / static_cast< double >(aSize.Width);
1606 nFormulaWidth -= nCountIteration;
1607 if ( nFormulaWidth < 0 )
1608 nFormulaWidth = 0;
1609 }
1610 }
1611 if( (aPos.Y + aSize.Height) > m_aPageReferenceSize.Height )
1612 aPos.Y = m_aPageReferenceSize.Height - aSize.Height;
1613 if( aPos.Y < 0 )
1614 aPos.Y = 0;
1615 if ( !bResizeEquation || nCountIteration == nMaxIteration-1 )
1616 xTextShape->setPosition(aPos); // if equation was not removed
1617 }
1618 }
1619}
1620
1621void VSeriesPlotter::setTimeResolutionOnXAxis( tools::Long TimeResolution, const Date& rNullDate )
1622{
1623 m_nTimeResolution = TimeResolution;
1624 m_aNullDate = rNullDate;
1625}
1626
1627// MinimumAndMaximumSupplier
1629{
1630 tools::Long nRet = css::chart::TimeUnit::YEAR;
1632 return nRet;
1633
1634 const std::vector<double>& rDateCategories = m_pExplicitCategoriesProvider->getDateCategories();
1635 if (rDateCategories.empty())
1636 return nRet;
1637
1638 std::vector<double>::const_iterator aIt = rDateCategories.begin(), aEnd = rDateCategories.end();
1639
1640 aIt = std::find_if(aIt, aEnd, [](const double& rDateCategory) { return !std::isnan(rDateCategory); });
1641 if (aIt == aEnd)
1642 return nRet;
1643
1644 Date aNullDate(30,12,1899);
1646 aNullDate = m_apNumberFormatterWrapper->getNullDate();
1647
1648 Date aPrevious(aNullDate); aPrevious.AddDays(rtl::math::approxFloor(*aIt));
1649 ++aIt;
1650 for(;aIt!=aEnd;++aIt)
1651 {
1652 if (std::isnan(*aIt))
1653 continue;
1654
1655 Date aCurrent(aNullDate); aCurrent.AddDays(rtl::math::approxFloor(*aIt));
1656 if( nRet == css::chart::TimeUnit::YEAR )
1657 {
1658 if( DateHelper::IsInSameYear( aPrevious, aCurrent ) )
1659 nRet = css::chart::TimeUnit::MONTH;
1660 }
1661 if( nRet == css::chart::TimeUnit::MONTH )
1662 {
1663 if( DateHelper::IsInSameMonth( aPrevious, aCurrent ) )
1664 nRet = css::chart::TimeUnit::DAY;
1665 }
1666 if( nRet == css::chart::TimeUnit::DAY )
1667 break;
1668 aPrevious=aCurrent;
1669 }
1670
1671 return nRet;
1672}
1674{
1675 double fMinimum, fMaximum;
1676 getMinimumAndMaximumX( fMinimum, fMaximum );
1677 return fMinimum;
1678}
1680{
1681 double fMinimum, fMaximum;
1682 getMinimumAndMaximumX( fMinimum, fMaximum );
1683 return fMaximum;
1684}
1685
1686double VSeriesPlotter::getMinimumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex )
1687{
1689 {
1690 double fMinY, fMaxY;
1691 getMinimumAndMaximumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex );
1692 return fMinY;
1693 }
1694
1695 double fMinimum = std::numeric_limits<double>::infinity();
1696 double fMaximum = -std::numeric_limits<double>::infinity();
1697 for(std::vector<VDataSeriesGroup> & rXSlots : m_aZSlots)
1698 {
1699 for(VDataSeriesGroup & rXSlot : rXSlots)
1700 {
1701 double fLocalMinimum, fLocalMaximum;
1702 rXSlot.calculateYMinAndMaxForCategoryRange(
1703 static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0
1704 , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0
1706 , fLocalMinimum, fLocalMaximum, nAxisIndex );
1707 if(fMaximum<fLocalMaximum)
1708 fMaximum=fLocalMaximum;
1709 if(fMinimum>fLocalMinimum)
1710 fMinimum=fLocalMinimum;
1711 }
1712 }
1713 if(std::isinf(fMinimum))
1714 return std::numeric_limits<double>::quiet_NaN();
1715 return fMinimum;
1716}
1717
1718double VSeriesPlotter::getMaximumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex )
1719{
1721 {
1722 double fMinY, fMaxY;
1723 getMinimumAndMaximumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex );
1724 return fMaxY;
1725 }
1726
1727 double fMinimum = std::numeric_limits<double>::infinity();
1728 double fMaximum = -std::numeric_limits<double>::infinity();
1729 for( std::vector< VDataSeriesGroup > & rXSlots : m_aZSlots)
1730 {
1731 for(VDataSeriesGroup & rXSlot : rXSlots)
1732 {
1733 double fLocalMinimum, fLocalMaximum;
1734 rXSlot.calculateYMinAndMaxForCategoryRange(
1735 static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0
1736 , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0
1738 , fLocalMinimum, fLocalMaximum, nAxisIndex );
1739 if(fMaximum<fLocalMaximum)
1740 fMaximum=fLocalMaximum;
1741 if(fMinimum>fLocalMinimum)
1742 fMinimum=fLocalMinimum;
1743 }
1744 }
1745 if(std::isinf(fMaximum))
1746 return std::numeric_limits<double>::quiet_NaN();
1747 return fMaximum;
1748}
1749
1751{
1752 //this is the default for all charts without a meaningful z axis
1753 return 1.0;
1754}
1756{
1757 if( m_nDimension!=3 || m_aZSlots.empty() )
1758 return getMinimumZ()+1;
1759 return m_aZSlots.size();
1760}
1761
1762namespace
1763{
1764 bool lcl_isValueAxis( sal_Int32 nDimensionIndex, bool bCategoryXAxis )
1765 {
1766 // default implementation: true for Y axes, and for value X axis
1767 if( nDimensionIndex == 0 )
1768 return !bCategoryXAxis;
1769 return nDimensionIndex == 1;
1770 }
1771}
1772
1774{
1775 return lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis );
1776}
1777
1778bool VSeriesPlotter::isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex )
1779{
1780 // do not expand axes in 3D charts
1781 return (m_nDimension < 3) && lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis );
1782}
1783
1784bool VSeriesPlotter::isExpandWideValuesToZero( sal_Int32 nDimensionIndex )
1785{
1786 // default implementation: only for Y axis
1787 return nDimensionIndex == 1;
1788}
1789
1790bool VSeriesPlotter::isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex )
1791{
1792 // default implementation: only for Y axis
1793 return nDimensionIndex == 1;
1794}
1795
1797{
1798 // default implementation: only for Y axis
1799 return nDimensionIndex == 1;
1800}
1801
1802void VSeriesPlotter::getMinimumAndMaximumX( double& rfMinimum, double& rfMaximum ) const
1803{
1804 rfMinimum = std::numeric_limits<double>::infinity();
1805 rfMaximum = -std::numeric_limits<double>::infinity();
1806
1807 for (auto const& ZSlot : m_aZSlots)
1808 {
1809 for (auto const& XSlot : ZSlot)
1810 {
1811 double fLocalMinimum, fLocalMaximum;
1812 XSlot.getMinimumAndMaximumX( fLocalMinimum, fLocalMaximum );
1813 if( !std::isnan(fLocalMinimum) && fLocalMinimum< rfMinimum )
1814 rfMinimum = fLocalMinimum;
1815 if( !std::isnan(fLocalMaximum) && fLocalMaximum> rfMaximum )
1816 rfMaximum = fLocalMaximum;
1817 }
1818 }
1819 if(std::isinf(rfMinimum))
1820 rfMinimum = std::numeric_limits<double>::quiet_NaN();
1821 if(std::isinf(rfMaximum))
1822 rfMaximum = std::numeric_limits<double>::quiet_NaN();
1823}
1824
1825void VSeriesPlotter::getMinimumAndMaximumYInContinuousXRange( double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const
1826{
1827 rfMinY = std::numeric_limits<double>::infinity();
1828 rfMaxY = -std::numeric_limits<double>::infinity();
1829
1830 for (auto const& ZSlot : m_aZSlots)
1831 {
1832 for (auto const& XSlot : ZSlot)
1833 {
1834 double fLocalMinimum, fLocalMaximum;
1835 XSlot.getMinimumAndMaximumYInContinuousXRange( fLocalMinimum, fLocalMaximum, fMinX, fMaxX, nAxisIndex );
1836 if( !std::isnan(fLocalMinimum) && fLocalMinimum< rfMinY )
1837 rfMinY = fLocalMinimum;
1838 if( !std::isnan(fLocalMaximum) && fLocalMaximum> rfMaxY )
1839 rfMaxY = fLocalMaximum;
1840 }
1841 }
1842 if(std::isinf(rfMinY))
1843 rfMinY = std::numeric_limits<double>::quiet_NaN();
1844 if(std::isinf(rfMaxY))
1845 rfMaxY = std::numeric_limits<double>::quiet_NaN();
1846}
1847
1849{
1850 sal_Int32 nRet = 0;
1851
1852 for (auto const& ZSlot : m_aZSlots)
1853 {
1854 for (auto const& XSlot : ZSlot)
1855 {
1856 sal_Int32 nPointCount = XSlot.getPointCount();
1857 if( nPointCount>nRet )
1858 nRet = nPointCount;
1859 }
1860 }
1861 return nRet;
1862}
1863
1865 const uno::Reference< util::XNumberFormatsSupplier > & xNumFmtSupplier )
1866{
1867 m_apNumberFormatterWrapper.reset( new NumberFormatterWrapper( xNumFmtSupplier ));
1868}
1869
1871{
1872 m_xColorScheme = xColorScheme;
1873}
1874
1876{
1877 m_pExplicitCategoriesProvider = pExplicitCategoriesProvider;
1878}
1879
1881{
1883 return m_nMaxPointCount;
1884
1885 sal_Int32 nRet = 0;
1886
1887 for (std::unique_ptr<VDataSeries> const & pSeries : m_aSeriesVector)
1888 {
1889 sal_Int32 nPointCount = pSeries->getTotalPointCount();
1890 if( nPointCount>nRet )
1891 nRet = nPointCount;
1892 }
1893 m_nMaxPointCount=nRet;
1894 m_aListOfCachedYValues.clear();
1897 return nRet;
1898}
1899
1901{
1902 sal_Int32 nRet = 0;
1903
1904 if (!m_aSeriesVector.empty())
1905 nRet = m_aSeriesVector[0]->getAttachedAxisIndex();
1906
1907 return nRet;
1908}
1909
1910void VDataSeriesGroup::getMinimumAndMaximumX( double& rfMinimum, double& rfMaximum ) const
1911{
1912
1913 rfMinimum = std::numeric_limits<double>::infinity();
1914 rfMaximum = -std::numeric_limits<double>::infinity();
1915
1916 for (std::unique_ptr<VDataSeries> const & pSeries : m_aSeriesVector)
1917 {
1918 sal_Int32 nPointCount = pSeries->getTotalPointCount();
1919 for(sal_Int32 nN=0;nN<nPointCount;nN++)
1920 {
1921 double fX = pSeries->getXValue( nN );
1922 if( std::isnan(fX) )
1923 continue;
1924 if(rfMaximum<fX)
1925 rfMaximum=fX;
1926 if(rfMinimum>fX)
1927 rfMinimum=fX;
1928 }
1929 }
1930 if(std::isinf(rfMinimum))
1931 rfMinimum = std::numeric_limits<double>::quiet_NaN();
1932 if(std::isinf(rfMaximum))
1933 rfMaximum = std::numeric_limits<double>::quiet_NaN();
1934}
1935
1936namespace {
1937
1952class PerXMinMaxCalculator
1953{
1954 typedef std::pair<double, double> MinMaxType;
1955 typedef std::map<size_t, MinMaxType> SeriesMinMaxType;
1956 typedef std::map<double, SeriesMinMaxType> GroupMinMaxType;
1957 typedef std::unordered_map<double, MinMaxType> TotalStoreType;
1958 GroupMinMaxType m_SeriesGroup;
1960
1961public:
1962 PerXMinMaxCalculator() : mnCurSeries(0) {}
1963
1964 void nextSeries() { ++mnCurSeries; }
1965
1966 void setValue(double fX, double fY)
1967 {
1968 SeriesMinMaxType* pStore = getByXValue(fX); // get storage for given X value.
1969 if (!pStore)
1970 // This shouldn't happen!
1971 return;
1972
1973 SeriesMinMaxType::iterator it = pStore->lower_bound(mnCurSeries);
1974 if (it != pStore->end() && !pStore->key_comp()(mnCurSeries, it->first))
1975 {
1976 MinMaxType& r = it->second;
1977 // A min-max pair already exists for this series. Update it.
1978 if (fY < r.first)
1979 r.first = fY;
1980 if (r.second < fY)
1981 r.second = fY;
1982 }
1983 else
1984 {
1985 // No existing pair. Insert a new one.
1986 pStore->insert(
1987 it, SeriesMinMaxType::value_type(
1988 mnCurSeries, MinMaxType(fY,fY)));
1989 }
1990 }
1991
1992 void getTotalRange(double& rfMin, double& rfMax) const
1993 {
1994 TotalStoreType aStore;
1995 getTotalStore(aStore);
1996
1997 if (aStore.empty())
1998 {
1999 rfMin = std::numeric_limits<double>::quiet_NaN();
2000 rfMax = std::numeric_limits<double>::quiet_NaN();
2001 return;
2002 }
2003
2004 TotalStoreType::const_iterator it = aStore.begin(), itEnd = aStore.end();
2005 rfMin = it->second.first;
2006 rfMax = it->second.second;
2007 for (++it; it != itEnd; ++it)
2008 {
2009 if (rfMin > it->second.first)
2010 rfMin = it->second.first;
2011 if (rfMax < it->second.second)
2012 rfMax = it->second.second;
2013 }
2014 }
2015
2016private:
2021 void getTotalStore(TotalStoreType& rStore) const
2022 {
2023 TotalStoreType aStore;
2024 for (auto const& it : m_SeriesGroup)
2025 {
2026 double fX = it.first;
2027
2028 const SeriesMinMaxType& rSeries = it.second;
2029 for (auto const& series : rSeries)
2030 {
2031 double fYMin = series.second.first, fYMax = series.second.second;
2032 TotalStoreType::iterator itr = aStore.find(fX);
2033 if (itr == aStore.end())
2034 // New min-max pair for give X value.
2035 aStore.emplace(fX, std::pair<double,double>(fYMin,fYMax));
2036 else
2037 {
2038 MinMaxType& r = itr->second;
2039 if (fYMin < r.first)
2040 r.first = fYMin; // min y-value
2041
2042 r.second += fYMax; // accumulative max y-value.
2043 }
2044 }
2045 }
2046 rStore.swap(aStore);
2047 }
2048
2049 SeriesMinMaxType* getByXValue(double fX)
2050 {
2051 GroupMinMaxType::iterator it = m_SeriesGroup.find(fX);
2052 if (it == m_SeriesGroup.end())
2053 {
2054 std::pair<GroupMinMaxType::iterator,bool> r =
2055 m_SeriesGroup.insert(std::make_pair(fX, SeriesMinMaxType{}));
2056
2057 if (!r.second)
2058 // insertion failed.
2059 return nullptr;
2060
2061 it = r.first;
2062 }
2063
2064 return &it->second;
2065 }
2066};
2067
2068}
2069
2071 double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const
2072{
2073 rfMinY = std::numeric_limits<double>::quiet_NaN();
2074 rfMaxY = std::numeric_limits<double>::quiet_NaN();
2075
2076 if (m_aSeriesVector.empty())
2077 // No data series. Bail out.
2078 return;
2079
2080 PerXMinMaxCalculator aRangeCalc;
2081 for (const std::unique_ptr<VDataSeries> & pSeries : m_aSeriesVector)
2082 {
2083 if (!pSeries)
2084 continue;
2085
2086 for (sal_Int32 i = 0, n = pSeries->getTotalPointCount(); i < n; ++i)
2087 {
2088 if (nAxisIndex != pSeries->getAttachedAxisIndex())
2089 continue;
2090
2091 double fX = pSeries->getXValue(i);
2092 if (std::isnan(fX))
2093 continue;
2094
2095 if (fX < fMinX || fX > fMaxX)
2096 // Outside specified X range. Skip it.
2097 continue;
2098
2099 double fY = pSeries->getYValue(i);
2100 if (std::isnan(fY))
2101 continue;
2102
2103 aRangeCalc.setValue(fX, fY);
2104 }
2105 aRangeCalc.nextSeries();
2106 }
2107
2108 aRangeCalc.getTotalRange(rfMinY, rfMaxY);
2109}
2110
2112 , bool bSeparateStackingForDifferentSigns
2113 , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex ) const
2114{
2115 assert(nCategoryIndex >= 0);
2116 assert(nCategoryIndex < getPointCount());
2117
2118 rfMinimumY = std::numeric_limits<double>::infinity();
2119 rfMaximumY = -std::numeric_limits<double>::infinity();
2120
2121 if(m_aSeriesVector.empty())
2122 return;
2123
2124 CachedYValues aCachedYValues = m_aListOfCachedYValues[nCategoryIndex][nAxisIndex];
2125 if( !aCachedYValues.m_bValuesDirty )
2126 {
2127 //return cached values
2128 rfMinimumY = aCachedYValues.m_fMinimumY;
2129 rfMaximumY = aCachedYValues.m_fMaximumY;
2130 return;
2131 }
2132
2133 double fTotalSum = std::numeric_limits<double>::quiet_NaN();
2134 double fPositiveSum = std::numeric_limits<double>::quiet_NaN();
2135 double fNegativeSum = std::numeric_limits<double>::quiet_NaN();
2136 double fFirstPositiveY = std::numeric_limits<double>::quiet_NaN();
2137 double fFirstNegativeY = std::numeric_limits<double>::quiet_NaN();
2138
2139 if( bSeparateStackingForDifferentSigns )
2140 {
2141 for (const std::unique_ptr<VDataSeries> & pSeries: m_aSeriesVector)
2142 {
2143 if( nAxisIndex != pSeries->getAttachedAxisIndex() )
2144 continue;
2145
2146 double fValueMinY = pSeries->getMinimumofAllDifferentYValues( nCategoryIndex );
2147 double fValueMaxY = pSeries->getMaximumofAllDifferentYValues( nCategoryIndex );
2148
2149 if( fValueMaxY >= 0 )
2150 {
2151 if( std::isnan( fPositiveSum ) )
2152 fPositiveSum = fFirstPositiveY = fValueMaxY;
2153 else
2154 fPositiveSum += fValueMaxY;
2155 }
2156 if( fValueMinY < 0 )
2157 {
2158 if(std::isnan( fNegativeSum ))
2159 fNegativeSum = fFirstNegativeY = fValueMinY;
2160 else
2161 fNegativeSum += fValueMinY;
2162 }
2163 }
2164 rfMinimumY = std::isnan( fNegativeSum ) ? fFirstPositiveY : fNegativeSum;
2165 rfMaximumY = std::isnan( fPositiveSum ) ? fFirstNegativeY : fPositiveSum;
2166 }
2167 else
2168 {
2169 for (const std::unique_ptr<VDataSeries> & pSeries: m_aSeriesVector)
2170 {
2171 if( nAxisIndex != pSeries->getAttachedAxisIndex() )
2172 continue;
2173
2174 double fValueMinY = pSeries->getMinimumofAllDifferentYValues( nCategoryIndex );
2175 double fValueMaxY = pSeries->getMaximumofAllDifferentYValues( nCategoryIndex );
2176
2177 if( std::isnan( fTotalSum ) )
2178 {
2179 rfMinimumY = fValueMinY;
2180 rfMaximumY = fTotalSum = fValueMaxY;
2181 }
2182 else
2183 {
2184 fTotalSum += fValueMaxY;
2185 if( rfMinimumY > fTotalSum )
2186 rfMinimumY = fTotalSum;
2187 if( rfMaximumY < fTotalSum )
2188 rfMaximumY = fTotalSum;
2189 }
2190 }
2191 }
2192
2193 aCachedYValues.m_fMinimumY = rfMinimumY;
2194 aCachedYValues.m_fMaximumY = rfMaximumY;
2195 aCachedYValues.m_bValuesDirty = false;
2196 m_aListOfCachedYValues[nCategoryIndex][nAxisIndex]=aCachedYValues;
2197}
2198
2200 sal_Int32 nStartCategoryIndex, sal_Int32 nEndCategoryIndex
2201 , bool bSeparateStackingForDifferentSigns
2202 , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex )
2203{
2204 //@todo maybe cache these values
2205 rfMinimumY = std::numeric_limits<double>::infinity();
2206 rfMaximumY = -std::numeric_limits<double>::infinity();
2207
2208 //iterate through the given categories
2209 if(nStartCategoryIndex<0)
2210 nStartCategoryIndex=0;
2211 const sal_Int32 nPointCount = getPointCount();//necessary to create m_aListOfCachedYValues
2212 if(nPointCount <= 0)
2213 return;
2214 if (nEndCategoryIndex >= nPointCount)
2215 nEndCategoryIndex = nPointCount - 1;
2216 if(nEndCategoryIndex<0)
2217 nEndCategoryIndex=0;
2218 for( sal_Int32 nCatIndex = nStartCategoryIndex; nCatIndex <= nEndCategoryIndex; nCatIndex++ )
2219 {
2220 double fMinimumY = std::numeric_limits<double>::quiet_NaN();
2221 double fMaximumY = std::numeric_limits<double>::quiet_NaN();
2222
2224 , bSeparateStackingForDifferentSigns, fMinimumY, fMaximumY, nAxisIndex );
2225
2226 if(rfMinimumY > fMinimumY)
2227 rfMinimumY = fMinimumY;
2228 if(rfMaximumY < fMaximumY)
2229 rfMaximumY = fMaximumY;
2230 }
2231}
2232
2234{
2235 double MinZ = m_pMainPosHelper->getLogicMinZ();
2236 double MaxZ = m_pMainPosHelper->getLogicMaxZ();
2237 m_pMainPosHelper->doLogicScaling( nullptr, nullptr, &MinZ );
2238 m_pMainPosHelper->doLogicScaling( nullptr, nullptr, &MaxZ );
2239 return FIXED_SIZE_FOR_3D_CHART_VOLUME/(MaxZ-MinZ);
2240}
2241
2242void VSeriesPlotter::addSecondaryValueScale( const ExplicitScaleData& rScale, sal_Int32 nAxisIndex )
2243{
2244 if( nAxisIndex<1 )
2245 return;
2246
2247 m_aSecondaryValueScales[nAxisIndex]=rScale;
2248}
2249
2251{
2252 PlottingPositionHelper* pRet = nullptr;
2253 if(nAxisIndex>0)
2254 {
2255 tSecondaryPosHelperMap::const_iterator aPosIt = m_aSecondaryPosHelperMap.find( nAxisIndex );
2256 if( aPosIt != m_aSecondaryPosHelperMap.end() )
2257 {
2258 pRet = aPosIt->second.get();
2259 }
2260 else if (m_pPosHelper)
2261 {
2262 tSecondaryValueScales::const_iterator aScaleIt = m_aSecondaryValueScales.find( nAxisIndex );
2263 if( aScaleIt != m_aSecondaryValueScales.end() )
2264 {
2265 m_aSecondaryPosHelperMap[nAxisIndex] = m_pPosHelper->createSecondaryPosHelper( aScaleIt->second );
2266 pRet = m_aSecondaryPosHelperMap[nAxisIndex].get();
2267 }
2268 }
2269 }
2270 if( !pRet )
2271 pRet = m_pMainPosHelper;
2273 return *pRet;
2274}
2275
2277{
2278}
2279
2281{
2282 for (std::vector<VDataSeriesGroup> const & rGroup : m_aZSlots)
2283 {
2284 if (!rGroup.empty())
2285 {
2286 if (!rGroup[0].m_aSeriesVector.empty())
2287 {
2288 VDataSeries* pSeries = rGroup[0].m_aSeriesVector[0].get();
2289 if (pSeries)
2290 return pSeries;
2291 }
2292 }
2293 }
2294 return nullptr;
2295}
2296
2297OUString VSeriesPlotter::getCategoryName( sal_Int32 nPointIndex ) const
2298{
2300 {
2302 if (nPointIndex >= 0 && nPointIndex < aCategories.getLength())
2303 {
2304 return aCategories[nPointIndex];
2305 }
2306 }
2307 return OUString();
2308}
2309
2310std::vector<VDataSeries const*> VSeriesPlotter::getAllSeries() const
2311{
2312 std::vector<VDataSeries const*> aAllSeries;
2313 for (std::vector<VDataSeriesGroup> const & rXSlot : m_aZSlots)
2314 {
2315 for(VDataSeriesGroup const & rGroup : rXSlot)
2316 {
2317 for (std::unique_ptr<VDataSeries> const & p : rGroup.m_aSeriesVector)
2318 aAllSeries.push_back(p.get());
2319 }
2320 }
2321 return aAllSeries;
2322}
2323
2324
2325std::vector<VDataSeries*> VSeriesPlotter::getAllSeries()
2326{
2327 std::vector<VDataSeries*> aAllSeries;
2328 for (std::vector<VDataSeriesGroup> const & rXSlot : m_aZSlots)
2329 {
2330 for(VDataSeriesGroup const & rGroup : rXSlot)
2331 {
2332 for (std::unique_ptr<VDataSeries> const & p : rGroup.m_aSeriesVector)
2333 aAllSeries.push_back(p.get());
2334 }
2335 }
2336 return aAllSeries;
2337}
2338
2340{
2341 std::vector<OUString> aRetVector;
2342
2343 OUString aRole;
2344 if (m_xChartTypeModel.is())
2345 aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel();
2346
2347 for (auto const& rGroup : m_aZSlots)
2348 {
2349 if (!rGroup.empty())
2350 {
2351 VDataSeriesGroup const & rSeriesGroup(rGroup[0]);
2352 if (!rSeriesGroup.m_aSeriesVector.empty())
2353 {
2354 VDataSeries const * pSeries = rSeriesGroup.m_aSeriesVector[0].get();
2355 rtl::Reference< DataSeries > xSeries( pSeries ? pSeries->getModel() : nullptr );
2356 if( xSeries.is() )
2357 {
2358 OUString aSeriesName( xSeries->getLabelForRole( aRole ) );
2359 aRetVector.push_back( aSeriesName );
2360 }
2361 }
2362 }
2363 }
2364 return comphelper::containerToSequence( aRetVector );
2365}
2366
2368{
2369 std::vector<OUString> aRetVector;
2370
2371 OUString aRole;
2372 if (m_xChartTypeModel.is())
2373 aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel();
2374
2375 for (VDataSeries const* pSeries : getAllSeries())
2376 {
2377 if (pSeries)
2378 {
2379 OUString aSeriesName(pSeries->getModel()->getLabelForRole(aRole));
2380 aRetVector.push_back(aSeriesName);
2381 }
2382 }
2383 return comphelper::containerToSequence(aRetVector);
2384}
2385
2386void VSeriesPlotter::setPageReferenceSize( const css::awt::Size & rPageRefSize )
2387{
2388 m_aPageReferenceSize = rPageRefSize;
2389
2390 // set reference size also at all data series
2391
2392 for (auto const & outer : m_aZSlots)
2393 for (VDataSeriesGroup const & rGroup : outer)
2394 {
2395 for (std::unique_ptr<VDataSeries> const & pSeries : rGroup.m_aSeriesVector)
2396 {
2397 pSeries->setPageReferenceSize(m_aPageReferenceSize);
2398 }
2399 }
2400}
2401
2402//better performance for big data
2404{
2405 m_aCoordinateSystemResolution = rCoordinateSystemResolution;
2406}
2407
2409{
2411}
2412
2414{
2415 return m_nDimension != 3;
2416}
2417
2418std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntries(
2419 const awt::Size& rEntryKeyAspectRatio
2420 , LegendPosition eLegendPosition
2421 , const Reference< beans::XPropertySet >& xTextProperties
2422 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
2423 , const Reference< uno::XComponentContext >& xContext
2424 , ChartModel& rModel
2425 )
2426{
2427 std::vector< ViewLegendEntry > aResult;
2428
2429 if( xTarget.is() )
2430 {
2431 rtl::Reference< Diagram > xDiagram = rModel.getFirstChartDiagram();
2432 rtl::Reference< BaseCoordinateSystem > xCooSys(xDiagram->getBaseCoordinateSystems()[0]);
2433 bool bSwapXAndY = false;
2434
2435 try
2436 {
2437 xCooSys->getPropertyValue( "SwapXAndYAxis" ) >>= bSwapXAndY;
2438 }
2439 catch( const uno::Exception& )
2440 {
2441 }
2442
2443 //iterate through all series
2444 bool bBreak = false;
2445 bool bFirstSeries = true;
2446
2447
2448 for (std::vector<VDataSeriesGroup> const & rGroupVector : m_aZSlots)
2449 {
2450 for (VDataSeriesGroup const & rGroup : rGroupVector)
2451 {
2452 for (std::unique_ptr<VDataSeries> const & pSeries : rGroup.m_aSeriesVector)
2453 {
2454 if (!pSeries)
2455 continue;
2456
2457 // "ShowLegendEntry"
2458 if (!pSeries->getModel()->getFastPropertyValue(PROP_DATASERIES_SHOW_LEGEND_ENTRY).get<sal_Bool>())
2459 {
2460 continue;
2461 }
2462
2463 std::vector<ViewLegendEntry> aSeriesEntries(
2465 rEntryKeyAspectRatio, *pSeries, xTextProperties,
2466 xTarget, xContext));
2467
2468 //add series entries to the result now
2469
2470 // use only the first series if VaryColorsByPoint is set for the first series
2471 if (bFirstSeries && pSeries->isVaryColorsByPoint())
2472 bBreak = true;
2473 bFirstSeries = false;
2474
2475 // add entries reverse if chart is stacked in y-direction and the legend position is right or left.
2476 // If the legend is top or bottom and we have a stacked bar-chart the normal order
2477 // is the correct one, unless the chart type is horizontal bar-chart.
2478 bool bReverse = false;
2479 if ( bSwapXAndY )
2480 {
2481 StackingDirection eStackingDirection( pSeries->getStackingDirection() );
2482 bReverse = ( eStackingDirection != StackingDirection_Y_STACKING );
2483 }
2484 else if ( eLegendPosition == LegendPosition_LINE_START || eLegendPosition == LegendPosition_LINE_END )
2485 {
2486 StackingDirection eStackingDirection( pSeries->getStackingDirection() );
2487 bReverse = ( eStackingDirection == StackingDirection_Y_STACKING );
2488 }
2489
2490 if (bReverse)
2491 aResult.insert( aResult.begin(), aSeriesEntries.begin(), aSeriesEntries.end() );
2492 else
2493 aResult.insert( aResult.end(), aSeriesEntries.begin(), aSeriesEntries.end() );
2494 }
2495 if (bBreak)
2496 return aResult;
2497 }
2498 }
2499 }
2500
2501 return aResult;
2502}
2503
2504std::vector<ViewLegendSymbol> VSeriesPlotter::createSymbols(const awt::Size& rEntryKeyAspectRatio
2505 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
2506 , const Reference<uno::XComponentContext>& xContext)
2507{
2508 std::vector<ViewLegendSymbol> aResult;
2509
2510 if( xTarget.is() )
2511 {
2512 bool bBreak = false;
2513 bool bFirstSeries = true;
2514
2515 for (std::vector<VDataSeriesGroup> const & rGroupVector : m_aZSlots)
2516 {
2517 for (VDataSeriesGroup const & rGroup : rGroupVector)
2518 {
2519 for (std::unique_ptr<VDataSeries> const & pSeries : rGroup.m_aSeriesVector)
2520 {
2521 if (!pSeries)
2522 continue;
2523
2524 std::vector<ViewLegendSymbol> aSeriesSymbols = createSymbolsForSeries(rEntryKeyAspectRatio, *pSeries, xTarget, xContext);
2525
2526 //add series entries to the result now
2527
2528 // use only the first series if VaryColorsByPoint is set for the first series
2529 if (bFirstSeries && pSeries->isVaryColorsByPoint())
2530 bBreak = true;
2531
2532 bFirstSeries = false;
2533
2534 aResult.insert(aResult.end(), aSeriesSymbols.begin(), aSeriesSymbols.end());
2535 }
2536 if (bBreak)
2537 return aResult;
2538 }
2539 }
2540 }
2541
2542 return aResult;
2543}
2544
2545namespace
2546{
2547bool lcl_HasVisibleLine( const uno::Reference< beans::XPropertySet >& xProps, bool& rbHasDashedLine )
2548{
2549 bool bHasVisibleLine = false;
2550 rbHasDashedLine = false;
2551 drawing::LineStyle aLineStyle = drawing::LineStyle_NONE;
2552 if( xProps.is() && ( xProps->getPropertyValue( "LineStyle") >>= aLineStyle ) )
2553 {
2554 if( aLineStyle != drawing::LineStyle_NONE )
2555 bHasVisibleLine = true;
2556 if( aLineStyle == drawing::LineStyle_DASH )
2557 rbHasDashedLine = true;
2558 }
2559 return bHasVisibleLine;
2560}
2561
2562bool lcl_HasRegressionCurves( const VDataSeries& rSeries, bool& rbHasDashedLine )
2563{
2564 bool bHasRegressionCurves = false;
2565 rtl::Reference< DataSeries > xRegrCont( rSeries.getModel() );
2566 for( const rtl::Reference< RegressionCurveModel > & rCurve : xRegrCont->getRegressionCurves2() )
2567 {
2568 bHasRegressionCurves = true;
2569 lcl_HasVisibleLine( rCurve, rbHasDashedLine );
2570 }
2571 return bHasRegressionCurves;
2572}
2573}
2575{
2577}
2578
2580{
2581 awt::Size aRet(1000,1000);
2582 if( m_nDimension==3 )
2583 return aRet;
2584
2585 bool bSeriesAllowsLines = (getLegendSymbolStyle() == LegendSymbolStyle::Line);
2586 bool bHasLines = false;
2587 bool bHasDashedLines = false;
2588 //iterate through all series
2589 for (VDataSeries* pSeries : getAllSeries())
2590 {
2591 if( bSeriesAllowsLines )
2592 {
2593 bool bCurrentDashed = false;
2594 if( lcl_HasVisibleLine( pSeries->getPropertiesOfSeries(), bCurrentDashed ) )
2595 {
2596 bHasLines = true;
2597 if( bCurrentDashed )
2598 {
2599 bHasDashedLines = true;
2600 break;
2601 }
2602 }
2603 }
2604 bool bRegressionHasDashedLines=false;
2605 if( lcl_HasRegressionCurves( *pSeries, bRegressionHasDashedLines ) )
2606 {
2607 bHasLines = true;
2608 if( bRegressionHasDashedLines )
2609 {
2610 bHasDashedLines = true;
2611 break;
2612 }
2613 }
2614 }
2615 if( bHasLines )
2616 {
2617 if( bHasDashedLines )
2618 aRet = awt::Size(1600,-1);
2619 else
2620 aRet = awt::Size(800,-1);
2621 }
2622 return aRet;
2623}
2624
2625uno::Any VSeriesPlotter::getExplicitSymbol( const VDataSeries& /*rSeries*/, sal_Int32 /*nPointIndex*/ )
2626{
2627 return uno::Any();
2628}
2629
2631 const awt::Size& rEntryKeyAspectRatio
2632 , const VDataSeries& rSeries
2633 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget )
2634{
2635
2636 LegendSymbolStyle eLegendSymbolStyle = getLegendSymbolStyle();
2637 uno::Any aExplicitSymbol( getExplicitSymbol( rSeries, -1 ) );
2638
2641
2642 // todo: maybe the property-style does not solely depend on the
2643 // legend-symbol type
2644 switch( eLegendSymbolStyle )
2645 {
2648 break;
2649 default:
2650 break;
2651 }
2653 xTarget, eLegendSymbolStyle,
2654 rSeries.getPropertiesOfSeries(), ePropType, aExplicitSymbol );
2655
2656 return xShape;
2657}
2658
2660 const awt::Size& rEntryKeyAspectRatio
2661 , const VDataSeries& rSeries
2662 , sal_Int32 nPointIndex
2663 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget )
2664{
2665
2666 LegendSymbolStyle eLegendSymbolStyle = getLegendSymbolStyle();
2667 uno::Any aExplicitSymbol( getExplicitSymbol(rSeries,nPointIndex) );
2668
2671
2672 // todo: maybe the property-style does not solely depend on the
2673 // legend-symbol type
2674 switch( eLegendSymbolStyle )
2675 {
2678 break;
2679 default:
2680 break;
2681 }
2682
2683 // the default properties for the data point are the data series properties.
2684 // If a data point has own attributes overwrite them
2686 Reference< beans::XPropertySet > xPointSet( xSeriesProps );
2687 if( rSeries.isAttributedDataPoint( nPointIndex ) )
2688 xPointSet.set( rSeries.getPropertiesOfPoint( nPointIndex ));
2689
2690 // if a data point has no own color use a color from the diagram's color scheme
2691 if( ! rSeries.hasPointOwnColor( nPointIndex ))
2692 {
2693 Reference< util::XCloneable > xCloneable( xPointSet,uno::UNO_QUERY );
2694 if( xCloneable.is() && m_xColorScheme.is() )
2695 {
2696 xPointSet.set( xCloneable->createClone(), uno::UNO_QUERY );
2697 Reference< container::XChild > xChild( xPointSet, uno::UNO_QUERY );
2698 if( xChild.is())
2699 xChild->setParent( xSeriesProps );
2700
2701 OSL_ASSERT( xPointSet.is());
2702 xPointSet->setPropertyValue(
2703 "Color", uno::Any( m_xColorScheme->getColorByIndex( nPointIndex )));
2704 }
2705 }
2706
2708 xTarget, eLegendSymbolStyle, xPointSet, ePropType, aExplicitSymbol );
2709
2710 return xShape;
2711}
2712
2714 const awt::Size& rEntryKeyAspectRatio
2715 , const VDataSeries& rSeries
2716 , const Reference< beans::XPropertySet >& xTextProperties
2717 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
2718 , const Reference< uno::XComponentContext >& xContext
2719 )
2720{
2721 std::vector< ViewLegendEntry > aResult;
2722
2723 if( ! ( xTarget.is() && xContext.is() ) )
2724 return aResult;
2725
2726 try
2727 {
2728 ViewLegendEntry aEntry;
2729 OUString aLabelText;
2730 bool bVaryColorsByPoint = rSeries.isVaryColorsByPoint();
2731 bool bIsPie = m_xChartTypeModel->getChartType().equalsIgnoreAsciiCase(
2733 try
2734 {
2735 if (bIsPie)
2736 {
2737 bool bDonut = false;
2738 // "UseRings"
2739 if ((m_xChartTypeModel->getFastPropertyValue(PROP_PIECHARTTYPE_USE_RINGS) >>= bDonut) && bDonut)
2740 bIsPie = false;
2741 }
2742 }
2743 catch (const uno::Exception&)
2744 {
2745 }
2746
2747 if (bVaryColorsByPoint || bIsPie)
2748 {
2749 Sequence< OUString > aCategoryNames;
2752 Sequence<sal_Int32> deletedLegendEntries;
2753 try
2754 {
2755 // "DeletedLegendEntries"
2756 rSeries.getModel()->getFastPropertyValue(PROP_DATASERIES_DELETED_LEGEND_ENTRIES) >>= deletedLegendEntries;
2757 }
2758 catch (const uno::Exception&)
2759 {
2760 }
2761 for( sal_Int32 nIdx=0; nIdx<aCategoryNames.getLength(); ++nIdx )
2762 {
2763 bool deletedLegendEntry = false;
2764 for (const auto& deletedLegendEntryIdx : std::as_const(deletedLegendEntries))
2765 {
2766 if (nIdx == deletedLegendEntryIdx)
2767 {
2768 deletedLegendEntry = true;
2769 break;
2770 }
2771 }
2772 if (deletedLegendEntry)
2773 continue;
2774
2775 // symbol
2777
2778 // create the symbol
2779 rtl::Reference< SvxShapeGroup > xShape = createLegendSymbolForPoint( rEntryKeyAspectRatio,
2780 rSeries, nIdx, xSymbolGroup );
2781
2782 // set CID to symbol for selection
2783 if( xShape.is() )
2784 {
2785 aEntry.xSymbol = xSymbolGroup;
2786
2789 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2790 ShapeFactory::setShapeName( xShape, aCID );
2791 }
2792
2793 // label
2794 aLabelText = aCategoryNames[nIdx];
2795 if( xShape.is() || !aLabelText.isEmpty() )
2796 {
2797 aEntry.xLabel = FormattedStringHelper::createFormattedString( aLabelText, xTextProperties );
2798 aResult.push_back(aEntry);
2799 }
2800 }
2801 }
2802 else
2803 {
2804 // symbol
2806
2807 // create the symbol
2809 rEntryKeyAspectRatio, rSeries, xSymbolGroup );
2810
2811 // set CID to symbol for selection
2812 if( xShape.is())
2813 {
2814 aEntry.xSymbol = xSymbolGroup;
2815
2817 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2818 ShapeFactory::setShapeName( xShape, aCID );
2819 }
2820
2821 // label
2822 aLabelText = rSeries.getModel()->getLabelForRole( m_xChartTypeModel.is() ? m_xChartTypeModel->getRoleOfSequenceForSeriesLabel() : "values-y");
2823 aEntry.xLabel = FormattedStringHelper::createFormattedString( aLabelText, xTextProperties );
2824
2825 aResult.push_back(aEntry);
2826 }
2827
2828 // don't show legend entry of regression curve & friends if this type of chart
2829 // doesn't support statistics #i63016#, fdo#37197
2831 return aResult;
2832
2833 rtl::Reference< DataSeries > xRegrCont = rSeries.getModel();
2834 if( xRegrCont.is())
2835 {
2836 const std::vector< rtl::Reference< RegressionCurveModel > > & aCurves = xRegrCont->getRegressionCurves2();
2837 sal_Int32 i = 0, nCount = aCurves.size();
2838 for( i=0; i<nCount; ++i )
2839 {
2840 //label
2841 OUString aResStr( RegressionCurveHelper::getUINameForRegressionCurve( aCurves[i] ) );
2842 replaceParamterInString( aResStr, u"%SERIESNAME", aLabelText );
2843 aEntry.xLabel = FormattedStringHelper::createFormattedString( aResStr, xTextProperties );
2844
2845 // symbol
2847
2848 // create the symbol
2850 xSymbolGroup, LegendSymbolStyle::Line,
2851 aCurves[i],
2853
2854 // set CID to symbol for selection
2855 if( xShape.is())
2856 {
2857 aEntry.xSymbol = xSymbolGroup;
2858
2859 bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurves[i] );
2860 ObjectType eObjectType = bAverageLine ? OBJECTTYPE_DATA_AVERAGE_LINE : OBJECTTYPE_DATA_CURVE;
2861 OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( eObjectType, i ) );
2863 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle );
2864 ShapeFactory::setShapeName( xShape, aCID );
2865 }
2866
2867 aResult.push_back(aEntry);
2868 }
2869 }
2870 }
2871 catch( const uno::Exception & )
2872 {
2873 DBG_UNHANDLED_EXCEPTION("chart2" );
2874 }
2875 return aResult;
2876}
2877
2878std::vector<ViewLegendSymbol> VSeriesPlotter::createSymbolsForSeries(
2879 const awt::Size& rEntryKeyAspectRatio
2880 , const VDataSeries& rSeries
2881 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
2882 , const Reference<uno::XComponentContext>& xContext)
2883{
2884 std::vector<ViewLegendSymbol> aResult;
2885
2886 if (!(xTarget.is() && xContext.is()))
2887 return aResult;
2888
2889 try
2890 {
2891 ViewLegendSymbol aEntry;
2892 // symbol
2894
2895 // create the symbol
2896 rtl::Reference<SvxShapeGroup> xShape = createLegendSymbolForSeries(rEntryKeyAspectRatio, rSeries, xSymbolGroup );
2897
2898 // set CID to symbol for selection
2899 if (xShape.is())
2900 {
2901 aEntry.xSymbol = xSymbolGroup;
2902 aResult.push_back(aEntry);
2903 }
2904 }
2905 catch (const uno::Exception &)
2906 {
2907 DBG_UNHANDLED_EXCEPTION("chart2" );
2908 }
2909 return aResult;
2910}
2911
2913 const rtl::Reference<ChartType>& xChartTypeModel
2914 , sal_Int32 nDimensionCount
2915 , bool bExcludingPositioning )
2916{
2917 if (!xChartTypeModel.is())
2918 return nullptr;
2919
2920 OUString aChartType = xChartTypeModel->getChartType();
2921
2922 VSeriesPlotter* pRet=nullptr;
2923 if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_COLUMN ) )
2924 pRet = new BarChart(xChartTypeModel,nDimensionCount);
2925 else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_BAR ) )
2926 pRet = new BarChart(xChartTypeModel,nDimensionCount);
2927 else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_AREA ) )
2928 pRet = new AreaChart(xChartTypeModel,nDimensionCount,true);
2929 else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_LINE ) )
2930 pRet = new AreaChart(xChartTypeModel,nDimensionCount,true,true);
2931 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER) )
2932 pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true);
2933 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE) )
2934 pRet = new BubbleChart(xChartTypeModel,nDimensionCount);
2935 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
2936 pRet = new PieChart(xChartTypeModel,nDimensionCount, bExcludingPositioning );
2937 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
2938 pRet = new NetChart(xChartTypeModel,nDimensionCount,true,std::make_unique<PolarPlottingPositionHelper>());
2939 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
2940 pRet = new NetChart(xChartTypeModel,nDimensionCount,false,std::make_unique<PolarPlottingPositionHelper>());
2941 else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
2942 pRet = new CandleStickChart(xChartTypeModel,nDimensionCount);
2943 else
2944 pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true);
2945 return pRet;
2946}
2947
2948} //namespace chart
2949
2950/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const sal_Unicode aSuperscriptFigures[10]
constexpr auto convertPointToMm100(N n)
size_t mnCurSeries
GroupMinMaxType m_SeriesGroup
double d
const LocaleDataWrapper & GetLocaleDataWrapper() const
static const AllSettings & GetSettings()
void AddDays(sal_Int32 nAddDays)
const OUString & getNumDecimalSep() const
B2DVector & normalize()
double getLength() const
TYPE getMaxX() const
TYPE getMinY() const
TYPE getMaxY() const
TYPE getX() const
TYPE getY() const
static bool isSeriesInFrontOfAxisLine(const rtl::Reference< ::chart::ChartType > &xChartType)
static bool isSupportingStatisticProperties(const rtl::Reference< ::chart::ChartType > &xChartType, sal_Int32 nDimensionCount)
static void clipPolygonAtRectangle(const css::drawing::PolyPolygonShape3D &rPolygon, const ::basegfx::B2DRectangle &rRectangle, css::drawing::PolyPolygonShape3D &aResult, bool bSplitPiecesToDifferentPolygons=true)
This class uses the Liang-Biarsky parametric line-clipping algorithm as described in: Computer Graphi...
static bool IsInSameMonth(const Date &rD1, const Date &rD2)
Definition: DateHelper.cxx:33
static bool IsInSameYear(const Date &rD1, const Date &rD2)
Definition: DateHelper.cxx:28
static sal_Int32 getPercentNumberFormat(const css::uno::Reference< css::util::XNumberFormatsSupplier > &xNumberFormatsSupplier)
const std::vector< double > & getDateCategories()
css::uno::Sequence< OUString > const & getSimpleCategories()
css::uno::Reference< css::chart2::data::XDataSequence > getOriginalCategories()
static rtl::Reference< ::chart::FormattedString > createFormattedString(const OUString &rString, const css::uno::Reference< css::beans::XPropertySet > &xTextProperties) noexcept
static void correctPositionForRotation(const rtl::Reference< SvxShapeText > &xShape2DText, LabelAlignment eLabelAlignment, const double fRotationAngle, bool bRotateAroundCenter)
static void changeTextAdjustment(tAnySequence &rPropValues, const tNameSequence &rPropNames, LabelAlignment eAlignment)
static OUString createChildParticleWithIndex(ObjectType eObjectType, sal_Int32 nIndex)
static OUString createClassifiedIdentifierForParticles(std::u16string_view rParentParticle, std::u16string_view rChildParticle, std::u16string_view rDragMethodServiceName=std::u16string_view(), std::u16string_view rDragParameterString=std::u16string_view())
static OUString addChildParticle(std::u16string_view rParticle, std::u16string_view rChildParticle)
static OUString createPointCID(std::u16string_view rPointCID_Stub, sal_Int32 nIndex)
This class provides methods for setting axis scales and for performing scene to screen transformation...
Definition: PlotterBase.hxx:43
PlottingPositionHelper * m_pPosHelper
Definition: PlotterBase.hxx:75
const sal_Int32 m_nDimension
Definition: PlotterBase.hxx:73
rtl::Reference< SvxShapeGroupAnyD > createGroupShape(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const OUString &rName=OUString())
Definition: PlotterBase.cxx:71
css::drawing::Direction3D getScaledLogicWidth() const
const std::vector< ExplicitScaleData > & getScales() const
::basegfx::B2DRectangle getScaledLogicClipDoubleRect() const
bool isLogicVisible(double fX, double fY, double fZ) const
virtual css::drawing::Position3D transformLogicToScene(double fX, double fY, double fZ, bool bClip) const
void doLogicScaling(double *pX, double *pY, double *pZ) const
virtual css::drawing::Position3D transformScaledLogicToScene(double fX, double fY, double fZ, bool bClip) const
void setTimeResolution(tools::Long nTimeResolution, const Date &rNullDate)
std::unique_ptr< PlottingPositionHelper > createSecondaryPosHelper(const ExplicitScaleData &rSecondaryScale)
static const tPropertyNameMap & getPropertyNameMapForLineProperties()
static void getPreparedTextShapePropertyLists(const css::uno::Reference< css::beans::XPropertySet > &xSourceProp, tNameSequence &rPropNames, tAnySequence &rPropValues)
adds line-, fill- and character properties and sets some suitable defaults for auto-grow properties
static void setMappedProperties(const css::uno::Reference< css::beans::XPropertySet > &xTarget, const css::uno::Reference< css::beans::XPropertySet > &xSource, const tPropertyNameMap &rMap)
static css::awt::Point getUpperLeftCornerOfAnchoredObject(css::awt::Point aPoint, css::awt::Size aObjectSize, css::drawing::Alignment aAnchor)
returns the upper left corner of an object that has size aObjectSize and where the point indicated by...
static void setShapeName(const rtl::Reference< SvxShape > &xShape, const OUString &rName)
static rtl::Reference< SvxShapeText > createText(const rtl::Reference< SvxShapeGroupAnyD > &xTarget2D, const OUString &rText, const tNameSequence &rPropNames, const tAnySequence &rPropValues, const css::uno::Any &rATransformation)
static rtl::Reference< SvxShapePolyPolygon > createLine2D(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const css::drawing::PointSequenceSequence &rPoints, const VLineProperties *pLineProperties=nullptr)
static rtl::Reference< SvxShapeGroup > createGroup2D(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const OUString &aName=OUString())
static css::uno::Any makeTransformation(const css::awt::Point &rScreenPosition2D, double fRotationAnglePi=0.0)
A list of series that have the same CoordinateSystem.
void calculateYMinAndMaxForCategory(sal_Int32 nCategoryIndex, bool bSeparateStackingForDifferentSigns, double &rfMinimumY, double &rfMaximumY, sal_Int32 nAxisIndex) const
std::vector< tCachedYValuesPerAxisIndexMap > m_aListOfCachedYValues
void getMinimumAndMaximumX(double &rfMinimum, double &rfMaximum) const
sal_Int32 getPointCount() const
sal_Int32 getAttachedAxisIndexForFirstSeries() const
void calculateYMinAndMaxForCategoryRange(sal_Int32 nStartCategoryIndex, sal_Int32 nEndCategoryIndex, bool bSeparateStackingForDifferentSigns, double &rfMinimumY, double &rfMaximumY, sal_Int32 nAxisIndex)
sal_Int32 getSeriesCount() const
void getMinimumAndMaximumYInContinuousXRange(double &rfMinY, double &rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex) const
std::vector< std::unique_ptr< VDataSeries > > m_aSeriesVector
void addSeries(std::unique_ptr< VDataSeries > pSeries)
rtl::Reference< SvxShapeGroupAnyD > m_xGroupShape
bool hasExplicitNumberFormat(sal_Int32 nPointIndex, bool bForPercentage) const
css::awt::Point getLabelPosition(css::awt::Point aTextShapePos, sal_Int32 nPointIndex) const
sal_Int32 getExplicitNumberFormat(sal_Int32 nPointIndex, bool bForPercentage) const
OUString getDataCurveCID(sal_Int32 nCurveIndex, bool bAverageLine) const
void getMinMaxXValue(double &fMin, double &fMax) const
double getYMeanValue() const
rtl::Reference< SvxShapeGroupAnyD > m_xErrorYBarsGroupShape
bool isVaryColorsByPoint() const
const css::uno::Reference< css::beans::XPropertySet > & getPropertiesOfSeries() const
css::uno::Sequence< double > const & getAllX() const
css::uno::Reference< css::beans::XPropertySet > getPropertiesOfPoint(sal_Int32 index) const
rtl::Reference< SvxShapeGroup > m_xLabelsGroupShape
rtl::Reference< SvxShapeGroupAnyD > m_xErrorXBarsGroupShape
css::chart2::DataPointLabel * getDataPointLabelIfLabel(sal_Int32 index) const
css::uno::Reference< css::beans::XPropertySet > getXErrorBarProperties(sal_Int32 index) const
const rtl::Reference<::chart::DataSeries > & getModel() const
rtl::Reference< SvxShapeGroupAnyD > m_xBackSubGroupShape
OUString getErrorBarsCID(bool bYError) const
bool isAttributedDataPoint(sal_Int32 index) const
const OUString & getLabelCID_Stub() const
const OUString & getCID() const
sal_Int32 detectNumberFormatKey(sal_Int32 nPointIndex) const
bool hasPointOwnColor(sal_Int32 index) const
double getXMeanValue() const
OUString getDataCurveEquationCID(sal_Int32 nCurveIndex) const
css::uno::Sequence< double > const & getAllY() const
css::uno::Reference< css::beans::XPropertySet > getYErrorBarProperties(sal_Int32 index) const
bool getTextLabelMultiPropertyLists(sal_Int32 index, tNameSequence *&pPropNames, tAnySequence *&pPropValues) const
bool isLabelCustomPos(sal_Int32 nPointIndex) const
const OUString & getSeriesParticle() const
rtl::Reference< SvxShapeGroupAnyD > m_xFrontSubGroupShape
OUString getLabelsCID() const
virtual std::vector< ViewLegendEntry > createLegendEntries(const css::awt::Size &rEntryKeyAspectRatio, css::chart2::LegendPosition eLegendPosition, const css::uno::Reference< css::beans::XPropertySet > &xTextProperties, const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const css::uno::Reference< css::uno::XComponentContext > &xContext, ChartModel &rModel) override
ExplicitCategoriesProvider * m_pExplicitCategoriesProvider
void getMinimumAndMaximumX(double &rfMinimum, double &rfMaximum) const
virtual bool isExpandWideValuesToZero(sal_Int32 nDimensionIndex) override
std::vector< ViewLegendSymbol > createSymbolsForSeries(const css::awt::Size &rEntryKeyAspectRatio, const VDataSeries &rSeries, const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const css::uno::Reference< css::uno::XComponentContext > &xContext)
void createErrorBar_X(const css::drawing::Position3D &rUnscaledLogicPosition, VDataSeries &rVDataSeries, sal_Int32 nPointIndex, const rtl::Reference< SvxShapeGroupAnyD > &xTarget)
css::uno::Sequence< sal_Int32 > m_aCoordinateSystemResolution
double getTransformedDepth() const
static rtl::Reference< SvxShapeGroup > getLabelsGroupShape(VDataSeries &rDataSeries, const rtl::Reference< SvxShapeGroupAnyD > &xTarget)
This method creates a 2D group shape for containing all text shapes needed for this series; the group...
void addSecondaryValueScale(const ExplicitScaleData &rScale, sal_Int32 nAxisIndex)
this enables you to handle series on the same x axis with different y axis the property AttachedAxisI...
std::vector< ViewLegendEntry > createLegendEntriesForSeries(const css::awt::Size &rEntryKeyAspectRatio, const VDataSeries &rSeries, const css::uno::Reference< css::beans::XPropertySet > &xTextProperties, const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const css::uno::Reference< css::uno::XComponentContext > &xContext)
rtl::Reference< SvxShapeText > createDataLabel(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, VDataSeries &rDataSeries, sal_Int32 nPointIndex, double fValue, double fSumValue, const css::awt::Point &rScreenPosition2D, LabelAlignment eAlignment, sal_Int32 nOffset=0, sal_Int32 nTextWidth=0)
This method creates a text shape for a label related to a data point and append it to the root text s...
css::awt::Size m_aPageReferenceSize
void createErrorBar_Y(const css::drawing::Position3D &rUnscaledLogicPosition, VDataSeries &rVDataSeries, sal_Int32 nPointIndex, const rtl::Reference< SvxShapeGroupAnyD > &xTarget, double const *pfScaledLogicX)
virtual double getMinimumX() override
virtual bool isExpandNarrowValuesTowardZero(sal_Int32 nDimensionIndex) override
css::uno::Reference< css::chart2::XColorScheme > m_xColorScheme
static void addErrorBorder(const css::drawing::Position3D &rPos0, const css::drawing::Position3D &rPos1, const rtl::Reference< SvxShapeGroupAnyD > &rTarget, const css::uno::Reference< css::beans::XPropertySet > &rErrorBorderProp)
rtl::Reference< SvxShapeGroup > createLegendSymbolForSeries(const css::awt::Size &rEntryKeyAspectRatio, const VDataSeries &rSeries, const rtl::Reference< SvxShapeGroupAnyD > &xTarget)
sal_Int32 getPointCount() const
virtual ~VSeriesPlotter() override
PlottingPositionHelper * m_pMainPosHelper
virtual double getMaximumYInRange(double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex) override
void setNumberFormatsSupplier(const css::uno::Reference< css::util::XNumberFormatsSupplier > &xNumFmtSupplier)
static VSeriesPlotter * createSeriesPlotter(const rtl::Reference< ::chart::ChartType > &xChartTypeModel, sal_Int32 nDimensionCount, bool bExcludingPositioning)
std::unique_ptr< NumberFormatterWrapper > m_apNumberFormatterWrapper
virtual bool isExpandIfValuesCloseToBorder(sal_Int32 nDimensionIndex) override
virtual tools::Long calculateTimeResolutionOnXAxis() override
virtual void setTimeResolutionOnXAxis(tools::Long nTimeResolution, const Date &rNullDate) override
rtl::Reference< SvxShapeGroupAnyD > getSeriesGroupShapeBackChild(VDataSeries *pDataSeries, const rtl::Reference< SvxShapeGroupAnyD > &xTarget)
virtual css::awt::Size getPreferredLegendKeyAspectRatio() override
tSecondaryValueScales m_aSecondaryValueScales
rtl::Reference< SvxShapeGroupAnyD > getSeriesGroupShape(VDataSeries *pDataSeries, const rtl::Reference< SvxShapeGroupAnyD > &xTarget)
void createRegressionCurveEquationShapes(const OUString &rEquationCID, const css::uno::Reference< css::beans::XPropertySet > &xEquationProperties, const rtl::Reference< SvxShapeGroupAnyD > &xEquationTarget, const css::uno::Reference< css::chart2::XRegressionCurveCalculator > &xRegressionCurveCalculator, css::awt::Point aDefaultPos)
virtual double getMaximumZ() override
std::vector< ViewLegendSymbol > createSymbols(const css::awt::Size &rEntryKeyAspectRatio, const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const css::uno::Reference< css::uno::XComponentContext > &xContext)
virtual PlottingPositionHelper & getPlottingPositionHelper(sal_Int32 nAxisIndex) const
void setPageReferenceSize(const css::awt::Size &rPageRefSize)
virtual bool shouldSnapRectToUsedArea()
void setCoordinateSystemResolution(const css::uno::Sequence< sal_Int32 > &rCoordinateSystemResolution)
virtual double getMaximumX() override
std::vector< std::vector< VDataSeriesGroup > > m_aZSlots
virtual LegendSymbolStyle getLegendSymbolStyle()
void createRegressionCurvesShapes(VDataSeries const &rVDataSeries, const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const rtl::Reference< SvxShapeGroupAnyD > &xEquationTarget, bool bMaySkipPointsInRegressionCalculation)
rtl::Reference< ::chart::ChartType > m_xChartTypeModel
virtual double getMinimumYInRange(double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex) override
virtual css::drawing::Direction3D getPreferredDiagramAspectRatio() const
a value <= 0 for a directions means that this direction can be stretched arbitrary
rtl::Reference< SvxShapeGroupAnyD > getSeriesGroupShapeFrontChild(VDataSeries *pDataSeries, const rtl::Reference< SvxShapeGroupAnyD > &xTarget)
virtual css::uno::Any getExplicitSymbol(const VDataSeries &rSeries, sal_Int32 nPointIndex)
void createErrorRectangle(const css::drawing::Position3D &rUnscaledLogicPosition, VDataSeries &rVDataSeries, sal_Int32 nIndex, const rtl::Reference< SvxShapeGroupAnyD > &rTarget, bool bUseXErrorData, bool bUseYErrorData)
virtual void rearrangeLabelToAvoidOverlapIfRequested(const css::awt::Size &rPageSize)
css::uno::Sequence< OUString > getAllSeriesNames() const
virtual bool isSeparateStackingForDifferentSigns(sal_Int32 nDimensionIndex) override
rtl::Reference< SvxShapeGroupAnyD > getErrorBarsGroupShape(VDataSeries &rDataSeries, const rtl::Reference< SvxShapeGroupAnyD > &xTarget, bool bYError)
virtual void addSeries(std::unique_ptr< VDataSeries > pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot)
A new series can be positioned relative to other series in a chart.
rtl::Reference< SvxShapeGroup > createLegendSymbolForPoint(const css::awt::Size &rEntryKeyAspectRatio, const VDataSeries &rSeries, sal_Int32 nPointIndex, const rtl::Reference< SvxShapeGroupAnyD > &xTarget)
css::uno::Sequence< OUString > getSeriesNames() const
tSecondaryPosHelperMap m_aSecondaryPosHelperMap
VDataSeries * getFirstSeries() const
std::vector< VDataSeries * > getAllSeries()
virtual bool isExpandBorderToIncrementRhythm(sal_Int32 nDimensionIndex) override
OUString getCategoryName(sal_Int32 nPointIndex) const
void createErrorBar(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const css::drawing::Position3D &rPos, const css::uno::Reference< css::beans::XPropertySet > &xErrorBarProperties, const VDataSeries &rVDataSeries, sal_Int32 nIndex, bool bVertical, const double *pfScaledLogicX)
creates two T-shaped error bars in both directions (up/down or left/right depending on the bVertical ...
void setColorScheme(const css::uno::Reference< css::chart2::XColorScheme > &xColorScheme)
virtual double getMinimumZ() override
OUString getLabelTextForValue(VDataSeries const &rDataSeries, sal_Int32 nPointIndex, double fValue, bool bAsPercentage)
This method returns a text string representation of the passed numeric value by exploiting a NumberFo...
void setExplicitCategoriesProvider(ExplicitCategoriesProvider *pExplicitCategoriesProvider)
void getMinimumAndMaximumYInContinuousXRange(double &rfMinY, double &rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex) const
int nCount
constexpr double FIXED_SIZE_FOR_3D_CHART_VOLUME
Definition: defines.hxx:22
#define TOOLS_WARN_EXCEPTION(area, stream)
#define DBG_UNHANDLED_EXCEPTION(...)
float u
Reference< XInterface > xTarget
FilterGroup & rTarget
sal_Int32 nIndex
void * p
sal_Int64 n
#define SAL_WARN_IF(condition, area, stream)
constexpr OUStringLiteral aData
constexpr double deg2rad(double v)
OOO_DLLPUBLIC_CHARTTOOLS::basegfx::B2IRectangle makeRectangle(const css::awt::Point &rPosition, const css::awt::Size &rSize)
OOO_DLLPUBLIC_CHARTTOOLS bool isMeanValueLine(const css::uno::Reference< css::chart2::XRegressionCurve > &xRegCurve)
OOO_DLLPUBLIC_CHARTTOOLS OUString getUINameForRegressionCurve(const css::uno::Reference< css::chart2::XRegressionCurve > &xCurve)
OOO_DLLPUBLIC_CHARTTOOLS double getStandardDeviation(const css::uno::Sequence< double > &rData)
OOO_DLLPUBLIC_CHARTTOOLS double getStandardError(const css::uno::Sequence< double > &rData)
OOO_DLLPUBLIC_CHARTTOOLS double getErrorFromDataSource(const css::uno::Reference< css::chart2::data::XDataSource > &xDataSource, sal_Int32 nIndex, bool bPositiveValue, bool bYError=true)
OOO_DLLPUBLIC_CHARTTOOLS double getVariance(const css::uno::Sequence< double > &rData)
Calculates 1/n * sum (x_i - x_mean)^2.
rtl::Reference< SvxShapeGroup > createSymbol(const css::awt::Size &rEntryKeyAspectRatio, const rtl::Reference< SvxShapeGroupAnyD > &rSymbolContainer, LegendSymbolStyle eStyle, const css::uno::Reference< css::beans::XPropertySet > &xLegendEntryProperties, PropertyType ePropertyType, const css::uno::Any &rExplicitSymbol)
@ OBJECTTYPE_LEGEND_ENTRY
@ OBJECTTYPE_DATA_POINT
@ OBJECTTYPE_DATA_AVERAGE_LINE
@ OBJECTTYPE_DATA_CURVE
OOO_DLLPUBLIC_CHARTTOOLS bool replaceParamterInString(OUString &rInOutResourceString, std::u16string_view rParamToReplace, std::u16string_view rReplaceWith)
@ PROP_PIECHARTTYPE_USE_RINGS
Definition: ChartType.hxx:43
OOO_DLLPUBLIC_CHARTTOOLS void AddPointToPoly(css::drawing::PolyPolygonShape3D &rPoly, const css::drawing::Position3D &rPos, sal_Int32 nSequenceIndex=0)
PolyPolygonShape3D + drawing::Position3D -> PolyPolygonShape3D.
css::uno::Sequence< OUString > tNameSequence
@ Box
A square box with border.
@ Line
A line like with a symbol.
static sal_Int32 lcl_getOUStringMaxLineLength(OUStringBuffer const &aString)
@ LABEL_ALIGN_CENTER
@ LABEL_ALIGN_RIGHT_TOP
@ LABEL_ALIGN_RIGHT_BOTTOM
@ LABEL_ALIGN_TOP
@ LABEL_ALIGN_LEFT_BOTTOM
@ LABEL_ALIGN_LEFT_TOP
@ LABEL_ALIGN_RIGHT
@ LABEL_ALIGN_LEFT
@ LABEL_ALIGN_BOTTOM
css::uno::Sequence< css::uno::Any > tAnySequence
OOO_DLLPUBLIC_CHARTTOOLS css::drawing::PointSequenceSequence PolyToPointSequence(const css::drawing::PolyPolygonShape3D &rPolyPolygon)
PolyPolygonShape3D -> drawing::PointSequenceSequence (2D)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
sal_Int32 getFieldType(guint nCol)
int i
line
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
long Long
RegError REGISTRY_CALLTYPE setValue(RegKeyHandle hKey, rtl_uString *keyName, RegValueType valueType, RegValue pData, sal_uInt32 valueSize)
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_COLUMN
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_PIE
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_SCATTER
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_BAR
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_AREA
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_LINE
constexpr OUStringLiteral CHART2_SERVICE_NAME_CHARTTYPE_NET
This structure contains the explicit values for a scale like Minimum and Maximum.
void initFromPropertySet(const css::uno::Reference< css::beans::XPropertySet > &xProp)
rtl::Reference< ::chart::FormattedString > xLabel
The descriptive text for a legend entry.
rtl::Reference< SvxShapeGroup > xSymbol
The legend symbol that represents a data series or other information contained in the legend.
rtl::Reference< SvxShapeGroup > xSymbol
The legend symbol that represents a data series or other information contained in the legend.
bool hasValue()
unsigned char sal_Bool
sal_uInt16 sal_Unicode
constexpr OUStringLiteral CHART_UNONAME_NUMFMT
Definition: unonames.hxx:20
constexpr OUStringLiteral CHART_UNONAME_CUSTOM_LABEL_FIELDS
Definition: unonames.hxx:36