Bug Summary

File:home/maarten/src/libreoffice/core/chart2/source/view/charttypes/VSeriesPlotter.cxx
Warning:line 2278, column 5
Returning null reference

Annotated Source Code

Press '?' to see keyboard shortcuts

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