File: | home/maarten/src/libreoffice/core/chart2/source/view/charttypes/VSeriesPlotter.cxx |
Warning: | line 2278, column 5 Returning null reference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
87 | namespace chart { | |||
88 | ||||
89 | using namespace ::com::sun::star; | |||
90 | using namespace ::com::sun::star::chart; | |||
91 | using namespace ::com::sun::star::chart2; | |||
92 | using ::com::sun::star::uno::Reference; | |||
93 | using ::com::sun::star::uno::Sequence; | |||
94 | ||||
95 | VDataSeriesGroup::CachedYValues::CachedYValues() | |||
96 | : m_bValuesDirty(true) | |||
97 | , m_fMinimumY(0.0) | |||
98 | , m_fMaximumY(0.0) | |||
99 | { | |||
100 | } | |||
101 | ||||
102 | VDataSeriesGroup::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 | ||||
111 | VDataSeriesGroup::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 | ||||
119 | VDataSeriesGroup::~VDataSeriesGroup() | |||
120 | { | |||
121 | } | |||
122 | ||||
123 | void VDataSeriesGroup::deleteSeries() | |||
124 | { | |||
125 | //delete all data series help objects: | |||
126 | m_aSeriesVector.clear(); | |||
127 | } | |||
128 | ||||
129 | void VDataSeriesGroup::addSeries( std::unique_ptr<VDataSeries> pSeries ) | |||
130 | { | |||
131 | m_aSeriesVector.push_back(std::move(pSeries)); | |||
132 | m_bMaxPointCountDirty=true; | |||
133 | } | |||
134 | ||||
135 | sal_Int32 VDataSeriesGroup::getSeriesCount() const | |||
136 | { | |||
137 | return m_aSeriesVector.size(); | |||
138 | } | |||
139 | ||||
140 | VSeriesPlotter::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 | ||||
159 | VSeriesPlotter::~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 | ||||
177 | void 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 | ||||
246 | drawing::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 | ||||
261 | void 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 | ||||
276 | uno::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 | ||||
289 | uno::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 | ||||
306 | uno::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 | ||||
321 | uno::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 | ||||
336 | uno::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 | ||||
354 | OUString 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 | ||||
397 | uno::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 | ||||
792 | namespace | |||
793 | { | |||
794 | double 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 | ||||
881 | void 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 | ||||
942 | drawing::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 | ||||
954 | void 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 | ||||
1098 | void 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 | ||||
1114 | void 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 | ||||
1259 | void 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 | ||||
1280 | void 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 | ||||
1302 | void 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 | ||||
1453 | static 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 | ||||
1472 | void 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 | ||||
1613 | void 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 | ||||
1623 | void VSeriesPlotter::setTimeResolutionOnXAxis( long TimeResolution, const Date& rNullDate ) | |||
1624 | { | |||
1625 | m_nTimeResolution = TimeResolution; | |||
1626 | m_aNullDate = rNullDate; | |||
1627 | } | |||
1628 | ||||
1629 | // MinimumAndMaximumSupplier | |||
1630 | long 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 | } | |||
1675 | double VSeriesPlotter::getMinimumX() | |||
1676 | { | |||
1677 | double fMinimum, fMaximum; | |||
1678 | getMinimumAndMaximumX( fMinimum, fMaximum ); | |||
1679 | return fMinimum; | |||
1680 | } | |||
1681 | double VSeriesPlotter::getMaximumX() | |||
1682 | { | |||
1683 | double fMinimum, fMaximum; | |||
1684 | getMinimumAndMaximumX( fMinimum, fMaximum ); | |||
1685 | return fMaximum; | |||
1686 | } | |||
1687 | ||||
1688 | double 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 | ||||
1721 | double 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 | ||||
1754 | double VSeriesPlotter::getMinimumZ() | |||
1755 | { | |||
1756 | //this is the default for all charts without a meaningful z axis | |||
1757 | return 1.0; | |||
1758 | } | |||
1759 | double VSeriesPlotter::getMaximumZ() | |||
1760 | { | |||
1761 | if( m_nDimension!=3 || m_aZSlots.empty() ) | |||
1762 | return getMinimumZ()+1; | |||
1763 | return m_aZSlots.size(); | |||
1764 | } | |||
1765 | ||||
1766 | namespace | |||
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 | ||||
1777 | bool VSeriesPlotter::isExpandBorderToIncrementRhythm( sal_Int32 nDimensionIndex ) | |||
1778 | { | |||
1779 | return lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis ); | |||
1780 | } | |||
1781 | ||||
1782 | bool 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 | ||||
1788 | bool VSeriesPlotter::isExpandWideValuesToZero( sal_Int32 nDimensionIndex ) | |||
1789 | { | |||
1790 | // default implementation: only for Y axis | |||
1791 | return nDimensionIndex == 1; | |||
1792 | } | |||
1793 | ||||
1794 | bool VSeriesPlotter::isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex ) | |||
1795 | { | |||
1796 | // default implementation: only for Y axis | |||
1797 | return nDimensionIndex == 1; | |||
1798 | } | |||
1799 | ||||
1800 | bool VSeriesPlotter::isSeparateStackingForDifferentSigns( sal_Int32 nDimensionIndex ) | |||
1801 | { | |||
1802 | // default implementation: only for Y axis | |||
1803 | return nDimensionIndex == 1; | |||
1804 | } | |||
1805 | ||||
1806 | void 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 | ||||
1829 | void 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 | ||||
1852 | sal_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 | ||||
1868 | void VSeriesPlotter::setNumberFormatsSupplier( | |||
1869 | const uno::Reference< util::XNumberFormatsSupplier > & xNumFmtSupplier ) | |||
1870 | { | |||
1871 | m_apNumberFormatterWrapper.reset( new NumberFormatterWrapper( xNumFmtSupplier )); | |||
1872 | } | |||
1873 | ||||
1874 | void VSeriesPlotter::setColorScheme( const uno::Reference< XColorScheme >& xColorScheme ) | |||
1875 | { | |||
1876 | m_xColorScheme = xColorScheme; | |||
1877 | } | |||
1878 | ||||
1879 | void VSeriesPlotter::setExplicitCategoriesProvider( ExplicitCategoriesProvider* pExplicitCategoriesProvider ) | |||
1880 | { | |||
1881 | m_pExplicitCategoriesProvider = pExplicitCategoriesProvider; | |||
1882 | } | |||
1883 | ||||
1884 | sal_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 | ||||
1904 | sal_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 | ||||
1914 | void 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 | ||||
1940 | namespace { | |||
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 | */ | |||
1956 | class 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 | ||||
1965 | public: | |||
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 | ||||
2019 | private: | |||
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 | ||||
2073 | void 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 | ||||
2114 | void 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 | ||||
2203 | void 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 | ||||
2237 | double 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 | ||||
2246 | void VSeriesPlotter::addSecondaryValueScale( const ExplicitScaleData& rScale, sal_Int32 nAxisIndex ) | |||
2247 | { | |||
2248 | if( nAxisIndex<1 ) | |||
2249 | return; | |||
2250 | ||||
2251 | m_aSecondaryValueScales[nAxisIndex]=rScale; | |||
2252 | } | |||
2253 | ||||
2254 | PlottingPositionHelper& VSeriesPlotter::getPlottingPositionHelper( sal_Int32 nAxisIndex ) const | |||
2255 | { | |||
2256 | PlottingPositionHelper* pRet = nullptr; | |||
2257 | if(nAxisIndex>0) | |||
| ||||
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
| |||
2275 | pRet = m_pMainPosHelper; | |||
2276 | if(pRet) | |||
2277 | pRet->setTimeResolution( m_nTimeResolution, m_aNullDate ); | |||
2278 | return *pRet; | |||
| ||||
2279 | } | |||
2280 | ||||
2281 | void VSeriesPlotter::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& /*rPageSize*/ ) | |||
2282 | { | |||
2283 | } | |||
2284 | ||||
2285 | VDataSeries* 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 | ||||
2302 | OUString 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 | ||||
2315 | uno::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 | ||||
2343 | void 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 | |||
2360 | void VSeriesPlotter::setCoordinateSystemResolution( const Sequence< sal_Int32 >& rCoordinateSystemResolution ) | |||
2361 | { | |||
2362 | m_aCoordinateSystemResolution = rCoordinateSystemResolution; | |||
2363 | } | |||
2364 | ||||
2365 | bool VSeriesPlotter::WantToPlotInFrontOfAxisLine() | |||
2366 | { | |||
2367 | return ChartTypeHelper::isSeriesInFrontOfAxisLine( m_xChartTypeModel ); | |||
2368 | } | |||
2369 | ||||
2370 | bool VSeriesPlotter::shouldSnapRectToUsedArea() | |||
2371 | { | |||
2372 | return m_nDimension != 3; | |||
2373 | } | |||
2374 | ||||
2375 | std::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 | ||||
2462 | std::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 | ||||
2476 | namespace | |||
2477 | { | |||
2478 | bool 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 | ||||
2493 | bool 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 | } | |||
2513 | LegendSymbolStyle VSeriesPlotter::getLegendSymbolStyle() | |||
2514 | { | |||
2515 | return LegendSymbolStyle::Box; | |||
2516 | } | |||
2517 | ||||
2518 | awt::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 | ||||
2564 | uno::Any VSeriesPlotter::getExplicitSymbol( const VDataSeries& /*rSeries*/, sal_Int32 /*nPointIndex*/ ) | |||
2565 | { | |||
2566 | return uno::Any(); | |||
2567 | } | |||
2568 | ||||
2569 | Reference< 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 | ||||
2599 | Reference< 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 | ||||
2654 | std::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 | ||||
2806 | VSeriesPlotter* 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: */ |