Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*
3 : : * This file is part of the LibreOffice project.
4 : : *
5 : : * This Source Code Form is subject to the terms of the Mozilla Public
6 : : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : : *
9 : : * This file incorporates work covered by the following license notice:
10 : : *
11 : : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : : * contributor license agreements. See the NOTICE file distributed
13 : : * with this work for additional information regarding copyright
14 : : * ownership. The ASF licenses this file to you under the Apache
15 : : * License, Version 2.0 (the "License"); you may not use this file
16 : : * except in compliance with the License. You may obtain a copy of
17 : : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : : */
19 : :
20 : : #include "ScaleAutomatism.hxx"
21 : : #include "macros.hxx"
22 : : #include "Tickmarks_Equidistant.hxx"
23 : : #include "DateHelper.hxx"
24 : : #include "DateScaling.hxx"
25 : : #include "AxisHelper.hxx"
26 : : #include <com/sun/star/chart/TimeUnit.hpp>
27 : :
28 : : #include <rtl/math.hxx>
29 : : #include <limits>
30 : :
31 : : //.............................................................................
32 : : namespace chart
33 : : {
34 : : //.............................................................................
35 : : using namespace ::com::sun::star;
36 : : using namespace ::com::sun::star::chart2;
37 : : using ::com::sun::star::chart::TimeUnit::DAY;
38 : : using ::com::sun::star::chart::TimeUnit::MONTH;
39 : : using ::com::sun::star::chart::TimeUnit::YEAR;
40 : :
41 : : const sal_Int32 MAXIMUM_MANUAL_INCREMENT_COUNT = 500;
42 : : const sal_Int32 MAXIMUM_SUB_INCREMENT_COUNT = 100;
43 : :
44 : 9363 : sal_Int32 lcl_getMaximumAutoIncrementCount( sal_Int32 nAxisType )
45 : : {
46 : 9363 : sal_Int32 nMaximumAutoIncrementCount = 10;
47 [ - + ]: 9363 : if( nAxisType==AxisType::DATE )
48 : 0 : nMaximumAutoIncrementCount = MAXIMUM_MANUAL_INCREMENT_COUNT;
49 : 9363 : return nMaximumAutoIncrementCount;
50 : : }
51 : :
52 : : namespace
53 : : {
54 : :
55 : 5972 : void lcl_ensureMaximumSubIncrementCount( sal_Int32& rnSubIntervalCount )
56 : : {
57 [ - + ]: 5972 : if( rnSubIntervalCount > MAXIMUM_SUB_INCREMENT_COUNT )
58 : 0 : rnSubIntervalCount = MAXIMUM_SUB_INCREMENT_COUNT;
59 : 5972 : }
60 : :
61 : : }//end anonymous namespace
62 : :
63 : :
64 : : //.............................................................................
65 : :
66 : 31553 : ExplicitScaleData::ExplicitScaleData()
67 : : : Minimum(0.0)
68 : : , Maximum(10.0)
69 : : , Origin(0.0)
70 : : , Orientation(::com::sun::star::chart2::AxisOrientation_MATHEMATICAL)
71 : : , Scaling()
72 : : , AxisType(::com::sun::star::chart2::AxisType::REALNUMBER)
73 : : , ShiftedCategoryPosition(false)
74 : : , TimeResolution(::com::sun::star::chart::TimeUnit::DAY)
75 : 31553 : , NullDate(30,12,1899)
76 : : {
77 : 31553 : }
78 : :
79 : 5972 : ExplicitSubIncrement::ExplicitSubIncrement()
80 : : : IntervalCount(2)
81 : 5972 : , PostEquidistant(true)
82 : : {
83 : 5972 : }
84 : :
85 : :
86 : 21803 : ExplicitIncrementData::ExplicitIncrementData()
87 : : : MajorTimeInterval(1,::com::sun::star::chart::TimeUnit::DAY)
88 : : , MinorTimeInterval(1,::com::sun::star::chart::TimeUnit::DAY)
89 : : , Distance(1.0)
90 : : , PostEquidistant(true)
91 : : , BaseValue(0.0)
92 : 21803 : , SubIncrements()
93 : : {
94 : 21803 : }
95 : :
96 : : //.............................................................................
97 : :
98 : 3984 : ScaleAutomatism::ScaleAutomatism( const ScaleData& rSourceScale, const Date& rNullDate )
99 : : : m_aSourceScale( rSourceScale )
100 : : , m_fValueMinimum( 0.0 )
101 : : , m_fValueMaximum( 0.0 )
102 : 3984 : , m_nMaximumAutoMainIncrementCount( lcl_getMaximumAutoIncrementCount( rSourceScale.AxisType ) )
103 : : , m_bExpandBorderToIncrementRhythm( false )
104 : : , m_bExpandIfValuesCloseToBorder( false )
105 : : , m_bExpandWideValuesToZero( false )
106 : : , m_bExpandNarrowValuesTowardZero( false )
107 : : , m_nTimeResolution(::com::sun::star::chart::TimeUnit::DAY)
108 : 7968 : , m_aNullDate(rNullDate)
109 : : {
110 : 3984 : ::rtl::math::setNan( &m_fValueMinimum );
111 : 3984 : ::rtl::math::setNan( &m_fValueMaximum );
112 : :
113 : 3984 : double fExplicitOrigin = 0.0;
114 [ + + ]: 3984 : if( m_aSourceScale.Origin >>= fExplicitOrigin )
115 : 169 : expandValueRange( fExplicitOrigin, fExplicitOrigin);
116 : 3984 : }
117 : 7968 : ScaleAutomatism::~ScaleAutomatism()
118 : : {
119 [ - + ]: 7968 : }
120 : :
121 : 4127 : void ScaleAutomatism::expandValueRange( double fMinimum, double fMaximum )
122 : : {
123 [ + + ][ + + ]: 4127 : if( (fMinimum < m_fValueMinimum) || ::rtl::math::isNan( m_fValueMinimum ) )
[ + + ]
124 : 2008 : m_fValueMinimum = fMinimum;
125 [ + + ][ + + ]: 4127 : if( (fMaximum > m_fValueMaximum) || ::rtl::math::isNan( m_fValueMaximum ) )
[ + + ]
126 : 2159 : m_fValueMaximum = fMaximum;
127 : 4127 : }
128 : :
129 : 3958 : void ScaleAutomatism::setAutoScalingOptions(
130 : : bool bExpandBorderToIncrementRhythm,
131 : : bool bExpandIfValuesCloseToBorder,
132 : : bool bExpandWideValuesToZero,
133 : : bool bExpandNarrowValuesTowardZero )
134 : : {
135 : : // if called multiple times, enable an option, if it is set in at least one call
136 : 3958 : m_bExpandBorderToIncrementRhythm |= bExpandBorderToIncrementRhythm;
137 : 3958 : m_bExpandIfValuesCloseToBorder |= bExpandIfValuesCloseToBorder;
138 : 3958 : m_bExpandWideValuesToZero |= bExpandWideValuesToZero;
139 : 3958 : m_bExpandNarrowValuesTowardZero |= bExpandNarrowValuesTowardZero;
140 : :
141 [ + + ]: 3958 : if( m_aSourceScale.AxisType==AxisType::PERCENT )
142 : 8 : m_bExpandIfValuesCloseToBorder = false;
143 : 3958 : }
144 : :
145 : 3890 : void ScaleAutomatism::setMaximumAutoMainIncrementCount( sal_Int32 nMaximumAutoMainIncrementCount )
146 : : {
147 [ + + ]: 3890 : if( nMaximumAutoMainIncrementCount < 2 )
148 : 45 : m_nMaximumAutoMainIncrementCount = 2; //#i82006
149 [ + + ]: 3845 : else if( nMaximumAutoMainIncrementCount > lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType ) )
150 : 1534 : m_nMaximumAutoMainIncrementCount = lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType );
151 : : else
152 : 2311 : m_nMaximumAutoMainIncrementCount = nMaximumAutoMainIncrementCount;
153 : 3890 : }
154 : :
155 : 0 : void ScaleAutomatism::setAutomaticTimeResolution( sal_Int32 nTimeResolution )
156 : : {
157 : 0 : m_nTimeResolution = nTimeResolution;
158 : 0 : }
159 : :
160 : 5972 : void ScaleAutomatism::calculateExplicitScaleAndIncrement(
161 : : ExplicitScaleData& rExplicitScale, ExplicitIncrementData& rExplicitIncrement ) const
162 : : {
163 : : // fill explicit scale
164 : 5972 : rExplicitScale.Orientation = m_aSourceScale.Orientation;
165 : 5972 : rExplicitScale.Scaling = m_aSourceScale.Scaling;
166 : 5972 : rExplicitScale.AxisType = m_aSourceScale.AxisType;
167 : 5972 : rExplicitScale.NullDate = m_aNullDate;
168 : :
169 : 5972 : bool bAutoMinimum = !(m_aSourceScale.Minimum >>= rExplicitScale.Minimum);
170 : 5972 : bool bAutoMaximum = !(m_aSourceScale.Maximum >>= rExplicitScale.Maximum);
171 : 5972 : bool bAutoOrigin = !(m_aSourceScale.Origin >>= rExplicitScale.Origin);
172 : :
173 : : // automatic scale minimum
174 [ + + ]: 5972 : if( bAutoMinimum )
175 : : {
176 [ + + ]: 5692 : if( m_aSourceScale.AxisType==AxisType::PERCENT )
177 : 16 : rExplicitScale.Minimum = 0.0;
178 [ + + ]: 5676 : else if( ::rtl::math::isNan( m_fValueMinimum ) )
179 : : {
180 [ - + ]: 835 : if( m_aSourceScale.AxisType==AxisType::DATE )
181 : 0 : rExplicitScale.Minimum = 36526.0; //1.1.2000
182 : : else
183 : 835 : rExplicitScale.Minimum = 0.0; //@todo get Minimum from scaling or from plotter????
184 : : }
185 : : else
186 : 4841 : rExplicitScale.Minimum = m_fValueMinimum;
187 : : }
188 : :
189 : : // automatic scale maximum
190 [ + + ]: 5972 : if( bAutoMaximum )
191 : : {
192 [ + + ]: 5684 : if( m_aSourceScale.AxisType==AxisType::PERCENT )
193 : 16 : rExplicitScale.Maximum = 1.0;
194 [ + + ]: 5668 : else if( ::rtl::math::isNan( m_fValueMaximum ) )
195 : : {
196 [ - + ]: 830 : if( m_aSourceScale.AxisType==AxisType::DATE )
197 : 0 : rExplicitScale.Maximum = 40179.0; //1.1.2010
198 : : else
199 : 830 : rExplicitScale.Maximum = 10.0; //@todo get Maximum from scaling or from plotter????
200 : : }
201 : : else
202 : 4838 : rExplicitScale.Maximum = m_fValueMaximum;
203 : : }
204 : :
205 : : //---------------------------------------------------------------
206 : : //fill explicit increment
207 : :
208 : 5972 : rExplicitScale.ShiftedCategoryPosition = m_aSourceScale.ShiftedCategoryPosition;
209 : 5972 : bool bIsLogarithm = false;
210 : :
211 : : //minimum and maximum of the ExplicitScaleData may be changed if allowed
212 [ - + ]: 5972 : if( m_aSourceScale.AxisType==AxisType::DATE )
213 : 0 : calculateExplicitIncrementAndScaleForDateTimeAxis( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
214 [ + + ][ + + ]: 5972 : else if( m_aSourceScale.AxisType==AxisType::CATEGORY || m_aSourceScale.AxisType==AxisType::SERIES )
215 : 2028 : calculateExplicitIncrementAndScaleForCategory( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
216 : : else
217 : : {
218 : 3944 : bIsLogarithm = AxisHelper::isLogarithmic( rExplicitScale.Scaling );
219 [ + + ]: 3944 : if( bIsLogarithm )
220 : 808 : calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
221 : : else
222 : 3136 : calculateExplicitIncrementAndScaleForLinear( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
223 : : }
224 : :
225 : : // automatic origin
226 [ + + ]: 5972 : if( bAutoOrigin )
227 : : {
228 : : // #i71415# automatic origin for logarithmic axis
229 [ + + ]: 5296 : double fDefaulOrigin = bIsLogarithm ? 1.0 : 0.0;
230 : :
231 [ + + ]: 5296 : if( fDefaulOrigin < rExplicitScale.Minimum )
232 : 2028 : fDefaulOrigin = rExplicitScale.Minimum;
233 [ - + ]: 3268 : else if( fDefaulOrigin > rExplicitScale.Maximum )
234 : 0 : fDefaulOrigin = rExplicitScale.Maximum;
235 : :
236 : 5296 : rExplicitScale.Origin = fDefaulOrigin;
237 : : }
238 : 5972 : }
239 : :
240 : 3972 : ScaleData ScaleAutomatism::getScale() const
241 : : {
242 : 3972 : return m_aSourceScale;
243 : : }
244 : :
245 : 0 : Date ScaleAutomatism::getNullDate() const
246 : : {
247 : 0 : return m_aNullDate;
248 : : }
249 : :
250 : : // private --------------------------------------------------------------------
251 : :
252 : 2028 : void ScaleAutomatism::calculateExplicitIncrementAndScaleForCategory(
253 : : ExplicitScaleData& rExplicitScale,
254 : : ExplicitIncrementData& rExplicitIncrement,
255 : : bool bAutoMinimum, bool bAutoMaximum ) const
256 : : {
257 : : // no scaling for categories
258 : 2028 : rExplicitScale.Scaling.clear();
259 : :
260 [ + + ]: 2028 : if( rExplicitScale.ShiftedCategoryPosition )
261 : 1822 : rExplicitScale.Maximum += 1.0;
262 : :
263 : : // ensure that at least one category is visible
264 [ - + ]: 2028 : if( rExplicitScale.Maximum <= rExplicitScale.Minimum )
265 : 0 : rExplicitScale.Maximum = rExplicitScale.Minimum + 1.0;
266 : :
267 : : // default increment settings
268 : 2028 : rExplicitIncrement.PostEquidistant = sal_True; // does not matter anyhow
269 : 2028 : rExplicitIncrement.Distance = 1.0; // category axis always have a main increment of 1
270 : 2028 : rExplicitIncrement.BaseValue = 0.0; // category axis always have a base of 0
271 : :
272 : : // automatic minimum and maximum
273 [ + - ][ + + ]: 2028 : if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
274 : 1 : rExplicitScale.Minimum = EquidistantTickFactory::getMinimumAtIncrement( rExplicitScale.Minimum, rExplicitIncrement );
275 [ + - ][ + + ]: 2028 : if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
276 : 1 : rExplicitScale.Maximum = EquidistantTickFactory::getMaximumAtIncrement( rExplicitScale.Maximum, rExplicitIncrement );
277 : :
278 : : //prevent performace killover
279 : 2028 : double fDistanceCount = ::rtl::math::approxFloor( (rExplicitScale.Maximum-rExplicitScale.Minimum) / rExplicitIncrement.Distance );
280 [ - + ]: 2028 : if( static_cast< sal_Int32 >( fDistanceCount ) > MAXIMUM_MANUAL_INCREMENT_COUNT )
281 : : {
282 : 0 : double fMinimumFloor = ::rtl::math::approxFloor( rExplicitScale.Minimum );
283 : 0 : double fMaximumCeil = ::rtl::math::approxCeil( rExplicitScale.Maximum );
284 : 0 : rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / MAXIMUM_MANUAL_INCREMENT_COUNT );
285 : : }
286 : :
287 : : //---------------------------------------------------------------
288 : : //fill explicit sub increment
289 : 2028 : sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
290 [ + + ]: 4056 : for( sal_Int32 nN=0; nN<nSubCount; nN++ )
291 : : {
292 [ + - ]: 2028 : ExplicitSubIncrement aExplicitSubIncrement;
293 : 2028 : const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
294 [ + - ]: 2028 : if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
295 : : {
296 : : //scaling dependent
297 : : //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
298 : 2028 : aExplicitSubIncrement.IntervalCount = 2;
299 : : }
300 : 2028 : lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
301 [ + - ]: 2028 : if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
302 : : {
303 : : //scaling dependent
304 : 2028 : aExplicitSubIncrement.PostEquidistant = sal_False;
305 : : }
306 [ + - ]: 2028 : rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
307 : : }
308 : 2028 : }
309 : :
310 : : //-----------------------------------------------------------------------------------------
311 : :
312 : 808 : void ScaleAutomatism::calculateExplicitIncrementAndScaleForLogarithmic(
313 : : ExplicitScaleData& rExplicitScale,
314 : : ExplicitIncrementData& rExplicitIncrement,
315 : : bool bAutoMinimum, bool bAutoMaximum ) const
316 : : {
317 : : // *** STEP 1: initialize the range data ***
318 : :
319 : 808 : const double fInputMinimum = rExplicitScale.Minimum;
320 : 808 : const double fInputMaximum = rExplicitScale.Maximum;
321 : :
322 : 808 : double fSourceMinimum = rExplicitScale.Minimum;
323 : 808 : double fSourceMaximum = rExplicitScale.Maximum;
324 : :
325 : : // set automatic PostEquidistant to true (maybe scaling dependent?)
326 : : // Note: scaling with PostEquidistant==false is untested and needs review
327 [ + - ]: 808 : if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
328 : 808 : rExplicitIncrement.PostEquidistant = sal_True;
329 : :
330 : : /* All following scaling code will operate on the logarithms of the source
331 : : values. In the last step, the original values will be restored. */
332 : 808 : uno::Reference< XScaling > xScaling = rExplicitScale.Scaling;
333 [ - + ]: 808 : if( !xScaling.is() )
334 [ # # ][ # # ]: 0 : xScaling.set( AxisHelper::createLogarithmicScaling() );
335 [ + - ][ + - ]: 808 : uno::Reference< XScaling > xInverseScaling = xScaling->getInverseScaling();
336 : :
337 [ + - ][ + - ]: 808 : fSourceMinimum = xScaling->doScaling( fSourceMinimum );
338 [ + + ]: 808 : if( !::rtl::math::isFinite( fSourceMinimum ) )
339 : 158 : fSourceMinimum = 0.0;
340 [ + + ]: 650 : else if( ::rtl::math::approxEqual( fSourceMinimum, ::rtl::math::approxFloor( fSourceMinimum ) ) )
341 : 176 : fSourceMinimum = ::rtl::math::approxFloor( fSourceMinimum );
342 : :
343 [ + - ][ + - ]: 808 : fSourceMaximum = xScaling->doScaling( fSourceMaximum );
344 [ - + ]: 808 : if( !::rtl::math::isFinite( fSourceMaximum ) )
345 : 0 : fSourceMaximum = 0.0;
346 [ + + ]: 808 : else if( ::rtl::math::approxEqual( fSourceMaximum, ::rtl::math::approxFloor( fSourceMaximum ) ) )
347 : 340 : fSourceMaximum = ::rtl::math::approxFloor( fSourceMaximum );
348 : :
349 : : /* If range is invalid (minimum greater than maximum), change one of the
350 : : variable limits to validate the range. In this step, a zero-sized range
351 : : is still allowed. */
352 [ - + ]: 808 : if( fSourceMinimum > fSourceMaximum )
353 : : {
354 : : // force changing the maximum, if both limits are fixed
355 [ # # ][ # # ]: 0 : if( bAutoMaximum || !bAutoMinimum )
356 : 0 : fSourceMaximum = fSourceMinimum;
357 : : else
358 : 0 : fSourceMinimum = fSourceMaximum;
359 : : }
360 : :
361 : : /* If maximum is less than 0 (and therefore minimum too), minimum and
362 : : maximum will be negated and swapped to make the following algorithms
363 : : easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
364 : : [2,5], and the latter will be swapped back later. The range [0,0] is
365 : : explicitly excluded from swapping (this would result in [-1,0] instead
366 : : of the expected [0,1]). */
367 [ - + ][ # # ]: 808 : bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
368 [ - + ]: 808 : if( bSwapAndNegateRange )
369 : : {
370 : 0 : double fTempValue = fSourceMinimum;
371 : 0 : fSourceMinimum = -fSourceMaximum;
372 : 0 : fSourceMaximum = -fTempValue;
373 : 0 : ::std::swap( bAutoMinimum, bAutoMaximum );
374 : : }
375 : :
376 : : // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
377 : :
378 : 808 : double fTempMinimum = fSourceMinimum;
379 : 808 : double fTempMaximum = fSourceMaximum;
380 : :
381 : : /* If minimum is variable and greater than 0 (and therefore maximum too),
382 : : means all original values are greater than 1 (or all values are less
383 : : than 1, and the range has been swapped above), then: */
384 [ + + ][ + + ]: 808 : if( bAutoMinimum && (fTempMinimum > 0.0) )
385 : : {
386 : : /* If minimum is less than 5 (i.e. original source values less than
387 : : B^5, B being the base of the scaling), or if minimum and maximum
388 : : are in different increment intervals (means, if minimum and maximum
389 : : are not both in the range [B^n,B^(n+1)] for a whole number n), set
390 : : minimum to 0, which results in B^0=1 on the axis. */
391 : 474 : double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
392 : 474 : double fMaximumFloor = ::rtl::math::approxFloor( fTempMaximum );
393 : : // handle the exact value B^(n+1) to be in the range [B^n,B^(n+1)]
394 [ + + ]: 474 : if( ::rtl::math::approxEqual( fTempMaximum, fMaximumFloor ) )
395 : 6 : fMaximumFloor -= 1.0;
396 : :
397 [ - + ][ # # ]: 474 : if( (fMinimumFloor < 5.0) || (fMinimumFloor < fMaximumFloor) )
398 : : {
399 [ + - ]: 948 : if( m_bExpandWideValuesToZero )
400 : 474 : fTempMinimum = 0.0;
401 : : }
402 : : /* Else (minimum and maximum are in one increment interval), expand
403 : : minimum toward 0 to make the 'shorter' data points visible. */
404 : : else
405 : : {
406 [ # # ]: 0 : if( m_bExpandNarrowValuesTowardZero )
407 : 474 : fTempMinimum -= 1.0;
408 : : }
409 : : }
410 : :
411 : : /* If range is still zero-sized (e.g. when minimum is fixed), set minimum
412 : : to 0, which makes the axis start/stop at the value 1. */
413 [ - + ]: 808 : if( fTempMinimum == fTempMaximum )
414 : : {
415 [ # # ][ # # ]: 0 : if( bAutoMinimum && (fTempMaximum > 0.0) )
416 : 0 : fTempMinimum = 0.0;
417 : : else
418 : 0 : fTempMaximum += 1.0; // always add one interval, even if maximum is fixed
419 : : }
420 : :
421 : : // *** STEP 3: calculate main interval size ***
422 : :
423 : : // base value (anchor position of the intervals), already scaled
424 [ + - ]: 808 : if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
425 : : {
426 : : //scaling dependent
427 : : //@maybe todo is this default also plotter dependent ??
428 [ + + ]: 808 : if( !bAutoMinimum )
429 : 176 : rExplicitIncrement.BaseValue = fTempMinimum;
430 [ + + ]: 632 : else if( !bAutoMaximum )
431 : 8 : rExplicitIncrement.BaseValue = fTempMaximum;
432 : : else
433 : 624 : rExplicitIncrement.BaseValue = 0.0;
434 : : }
435 : :
436 : : // calculate automatic interval
437 : 808 : bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
438 [ + + ]: 808 : if( bAutoDistance )
439 : 656 : rExplicitIncrement.Distance = 0.0;
440 : :
441 : : /* Restrict number of allowed intervals with user-defined distance to
442 : : MAXIMUM_MANUAL_INCREMENT_COUNT. */
443 : : sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
444 [ + + ]: 808 : m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
445 : :
446 : : // repeat calculation until number of intervals are valid
447 : 808 : bool bNeedIteration = true;
448 : 808 : bool bHasCalculatedDistance = false;
449 [ + + ]: 1616 : while( bNeedIteration )
450 : : {
451 [ + + ]: 808 : if( bAutoDistance )
452 : : {
453 : : // first iteration: calculate interval size from axis limits
454 [ + - ]: 656 : if( !bHasCalculatedDistance )
455 : : {
456 : 656 : double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
457 : 656 : double fMaximumCeil = ::rtl::math::approxCeil( fTempMaximum );
458 : 656 : rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / nMaxMainIncrementCount );
459 : : }
460 : : else
461 : : {
462 : : // following iterations: increase distance
463 : 0 : rExplicitIncrement.Distance += 1.0;
464 : : }
465 : :
466 : : // for next iteration: distance calculated -> use else path to increase
467 : 656 : bHasCalculatedDistance = true;
468 : : }
469 : :
470 : : // *** STEP 4: additional space above or below the data points ***
471 : :
472 : 808 : double fAxisMinimum = fTempMinimum;
473 : 808 : double fAxisMaximum = fTempMaximum;
474 : :
475 : : // round to entire multiples of the distance and add additional space
476 [ + + ][ + + ]: 808 : if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
477 : : {
478 [ + - ]: 474 : fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );
479 : :
480 : : //ensure valid values after scaling #i100995#
481 [ - + ]: 474 : if( !bAutoDistance )
482 : : {
483 [ # # ][ # # ]: 0 : double fCheck = xInverseScaling->doScaling( fAxisMinimum );
484 [ # # ][ # # ]: 0 : if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 )
[ # # ]
485 : : {
486 : 0 : bAutoDistance = true;
487 : 0 : bHasCalculatedDistance = false;
488 : 0 : continue;
489 : : }
490 : : }
491 : : }
492 [ + + ][ + + ]: 808 : if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
493 : : {
494 [ + - ]: 468 : fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
495 : :
496 : : //ensure valid values after scaling #i100995#
497 [ - + ]: 468 : if( !bAutoDistance )
498 : : {
499 [ # # ][ # # ]: 0 : double fCheck = xInverseScaling->doScaling( fAxisMaximum );
500 [ # # ][ # # ]: 0 : if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 )
[ # # ]
501 : : {
502 : 0 : bAutoDistance = true;
503 : 0 : bHasCalculatedDistance = false;
504 : 0 : continue;
505 : : }
506 : : }
507 : : }
508 : :
509 : : // set the resulting limits (swap back to negative range if needed)
510 [ - + ]: 808 : if( bSwapAndNegateRange )
511 : : {
512 : 0 : rExplicitScale.Minimum = -fAxisMaximum;
513 : 0 : rExplicitScale.Maximum = -fAxisMinimum;
514 : : }
515 : : else
516 : : {
517 : 808 : rExplicitScale.Minimum = fAxisMinimum;
518 : 808 : rExplicitScale.Maximum = fAxisMaximum;
519 : : }
520 : :
521 : : /* If the number of intervals is too high (e.g. due to invalid fixed
522 : : distance or due to added space above or below data points),
523 : : calculate again with increased distance. */
524 : 808 : double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
525 : 808 : bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
526 : : // if manual distance is invalid, trigger automatic calculation
527 [ - + ]: 808 : if( bNeedIteration )
528 : 0 : bAutoDistance = true;
529 : :
530 : : // convert limits back to logarithmic scale
531 [ + - ][ + - ]: 808 : rExplicitScale.Minimum = xInverseScaling->doScaling( rExplicitScale.Minimum );
532 [ + - ][ + - ]: 808 : rExplicitScale.Maximum = xInverseScaling->doScaling( rExplicitScale.Maximum );
533 : :
534 : : //ensure valid values after scaling #i100995#
535 [ + - ][ - + ]: 808 : if( !::rtl::math::isFinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0)
[ - + ]
536 : : {
537 : 0 : rExplicitScale.Minimum = fInputMinimum;
538 [ # # ][ # # ]: 0 : if( !::rtl::math::isFinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0 )
[ # # ]
539 : 0 : rExplicitScale.Minimum = 1.0;
540 : : }
541 [ + - ][ - + ]: 808 : if( !::rtl::math::isFinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
[ - + ]
542 : : {
543 : 0 : rExplicitScale.Maximum= fInputMaximum;
544 [ # # ][ # # ]: 0 : if( !::rtl::math::isFinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
[ # # ]
545 : 0 : rExplicitScale.Maximum = 10.0;
546 : : }
547 [ - + ]: 808 : if( rExplicitScale.Maximum < rExplicitScale.Minimum )
548 : 0 : ::std::swap( rExplicitScale.Maximum, rExplicitScale.Minimum );
549 : : }
550 : :
551 : : //---------------------------------------------------------------
552 : : //fill explicit sub increment
553 : 808 : sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
554 [ + + ]: 1616 : for( sal_Int32 nN=0; nN<nSubCount; nN++ )
555 : : {
556 [ + - ]: 808 : ExplicitSubIncrement aExplicitSubIncrement;
557 : 808 : const SubIncrement& rSubIncrement = m_aSourceScale.IncrementData.SubIncrements[nN];
558 [ + + ]: 808 : if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
559 : : {
560 : : //scaling dependent
561 : : //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
562 : 648 : aExplicitSubIncrement.IntervalCount = 9;
563 : : }
564 : 808 : lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
565 [ + - ]: 808 : if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
566 : : {
567 : : //scaling dependent
568 : 808 : aExplicitSubIncrement.PostEquidistant = sal_False;
569 : : }
570 [ + - ]: 808 : rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
571 : 808 : }
572 : 808 : }
573 : :
574 : : //-----------------------------------------------------------------------------------------
575 : :
576 : 0 : void ScaleAutomatism::calculateExplicitIncrementAndScaleForDateTimeAxis(
577 : : ExplicitScaleData& rExplicitScale,
578 : : ExplicitIncrementData& rExplicitIncrement,
579 : : bool bAutoMinimum, bool bAutoMaximum ) const
580 : : {
581 [ # # ]: 0 : Date aMinDate(m_aNullDate); aMinDate += static_cast<long>(::rtl::math::approxFloor(rExplicitScale.Minimum));
582 [ # # ]: 0 : Date aMaxDate(m_aNullDate); aMaxDate += static_cast<long>(::rtl::math::approxFloor(rExplicitScale.Maximum));
583 : 0 : rExplicitIncrement.PostEquidistant = sal_False;
584 : :
585 [ # # ]: 0 : if( aMinDate > aMaxDate )
586 : : {
587 [ # # ]: 0 : std::swap(aMinDate,aMaxDate);
588 : : }
589 : :
590 [ # # ]: 0 : if( !(m_aSourceScale.TimeIncrement.TimeResolution >>= rExplicitScale.TimeResolution) )
591 : 0 : rExplicitScale.TimeResolution = m_nTimeResolution;
592 : :
593 [ # # ][ # # ]: 0 : rExplicitScale.Scaling = new DateScaling(m_aNullDate,rExplicitScale.TimeResolution,false);
[ # # ]
594 : :
595 : : // choose min and max suitable to time resolution
596 [ # # # # ]: 0 : switch( rExplicitScale.TimeResolution )
597 : : {
598 : : case DAY:
599 [ # # ]: 0 : if( rExplicitScale.ShiftedCategoryPosition )
600 [ # # ]: 0 : aMaxDate++;//for explicit scales we need one interval more (maximum excluded)
601 : 0 : break;
602 : : case MONTH:
603 [ # # ]: 0 : aMinDate.SetDay(1);
604 [ # # ]: 0 : aMaxDate.SetDay(1);
605 [ # # ]: 0 : if( rExplicitScale.ShiftedCategoryPosition )
606 [ # # ]: 0 : aMaxDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
607 [ # # ][ # # ]: 0 : if( DateHelper::IsLessThanOneMonthAway( aMinDate, aMaxDate ) )
608 : : {
609 [ # # ][ # # ]: 0 : if( bAutoMaximum || !bAutoMinimum )
610 [ # # ]: 0 : aMaxDate = DateHelper::GetDateSomeMonthsAway(aMinDate,1);
611 : : else
612 [ # # ]: 0 : aMinDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,-1);
613 : : }
614 : 0 : break;
615 : : case YEAR:
616 [ # # ]: 0 : aMinDate.SetDay(1);
617 [ # # ]: 0 : aMinDate.SetMonth(1);
618 [ # # ]: 0 : aMaxDate.SetDay(1);
619 [ # # ]: 0 : aMaxDate.SetMonth(1);
620 [ # # ]: 0 : if( rExplicitScale.ShiftedCategoryPosition )
621 [ # # ]: 0 : aMaxDate = DateHelper::GetDateSomeYearsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
622 [ # # ][ # # ]: 0 : if( DateHelper::IsLessThanOneYearAway( aMinDate, aMaxDate ) )
623 : : {
624 [ # # ][ # # ]: 0 : if( bAutoMaximum || !bAutoMinimum )
625 [ # # ]: 0 : aMaxDate = DateHelper::GetDateSomeYearsAway(aMinDate,1);
626 : : else
627 [ # # ]: 0 : aMinDate = DateHelper::GetDateSomeYearsAway(aMaxDate,-1);
628 : : }
629 : 0 : break;
630 : : }
631 : :
632 : : // set the resulting limits (swap back to negative range if needed)
633 [ # # ]: 0 : rExplicitScale.Minimum = aMinDate - m_aNullDate;
634 [ # # ]: 0 : rExplicitScale.Maximum = aMaxDate - m_aNullDate;
635 : :
636 [ # # ]: 0 : bool bAutoMajor = !(m_aSourceScale.TimeIncrement.MajorTimeInterval >>= rExplicitIncrement.MajorTimeInterval);
637 [ # # ]: 0 : bool bAutoMinor = !(m_aSourceScale.TimeIncrement.MinorTimeInterval >>= rExplicitIncrement.MinorTimeInterval);
638 : :
639 : : sal_Int32 nMaxMainIncrementCount = bAutoMajor ?
640 [ # # ]: 0 : m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
641 [ # # ]: 0 : if( nMaxMainIncrementCount > 1 )
642 : 0 : nMaxMainIncrementCount--;
643 : :
644 : :
645 : : //choose major time interval:
646 [ # # ]: 0 : long nDayCount = (aMaxDate-aMinDate);
647 : 0 : long nMainIncrementCount = 1;
648 [ # # ]: 0 : if( !bAutoMajor )
649 : : {
650 : 0 : long nIntervalDayCount = rExplicitIncrement.MajorTimeInterval.Number;
651 [ # # ]: 0 : if( rExplicitIncrement.MajorTimeInterval.TimeUnit < rExplicitScale.TimeResolution )
652 : 0 : rExplicitIncrement.MajorTimeInterval.TimeUnit = rExplicitScale.TimeResolution;
653 [ # # # # ]: 0 : switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
654 : : {
655 : : case DAY:
656 : 0 : break;
657 : : case MONTH:
658 : 0 : nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
659 : 0 : break;
660 : : case YEAR:
661 : 0 : nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
662 : 0 : break;
663 : : }
664 : 0 : nMainIncrementCount = nDayCount/nIntervalDayCount;
665 [ # # ]: 0 : if( nMainIncrementCount > nMaxMainIncrementCount )
666 : 0 : bAutoMajor = true;
667 : : }
668 [ # # ]: 0 : if( bAutoMajor )
669 : : {
670 : 0 : long nNumer = 1;
671 : 0 : long nIntervalDays = nDayCount / nMaxMainIncrementCount;
672 : 0 : double nDaysPerInterval = 1.0;
673 [ # # ][ # # ]: 0 : if( nIntervalDays>365 || YEAR==rExplicitScale.TimeResolution )
674 : : {
675 : 0 : rExplicitIncrement.MajorTimeInterval.TimeUnit = YEAR;
676 : 0 : nDaysPerInterval = 365.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
677 : : }
678 [ # # ][ # # ]: 0 : else if( nIntervalDays>31 || MONTH==rExplicitScale.TimeResolution )
679 : : {
680 : 0 : rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
681 : 0 : nDaysPerInterval = 31.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
682 : : }
683 : : else
684 : : {
685 : 0 : rExplicitIncrement.MajorTimeInterval.TimeUnit = DAY;
686 : 0 : nDaysPerInterval = 1.0;
687 : : }
688 : :
689 : 0 : nNumer = static_cast<sal_Int32>( rtl::math::approxCeil( nIntervalDays/nDaysPerInterval ) );
690 [ # # ]: 0 : if(nNumer<=0)
691 : 0 : nNumer=1;
692 [ # # ]: 0 : if( rExplicitIncrement.MajorTimeInterval.TimeUnit == DAY )
693 : : {
694 [ # # ][ # # ]: 0 : if( nNumer>2 && nNumer<7 )
695 : 0 : nNumer=7;
696 [ # # ]: 0 : else if( nNumer>7 )
697 : : {
698 : 0 : rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
699 : 0 : nDaysPerInterval = 31.0;
700 : 0 : nNumer = static_cast<sal_Int32>( rtl::math::approxCeil( nIntervalDays/nDaysPerInterval ) );
701 [ # # ]: 0 : if(nNumer<=0)
702 : 0 : nNumer=1;
703 : : }
704 : : }
705 : 0 : rExplicitIncrement.MajorTimeInterval.Number = nNumer;
706 : 0 : nMainIncrementCount = static_cast<long>(nDayCount/(nNumer*nDaysPerInterval));
707 : : }
708 : :
709 : : //choose minor time interval:
710 [ # # ]: 0 : if( !bAutoMinor )
711 : : {
712 [ # # ]: 0 : if( rExplicitIncrement.MinorTimeInterval.TimeUnit > rExplicitIncrement.MajorTimeInterval.TimeUnit )
713 : 0 : rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
714 : 0 : long nIntervalDayCount = rExplicitIncrement.MinorTimeInterval.Number;
715 [ # # # # ]: 0 : switch( rExplicitIncrement.MinorTimeInterval.TimeUnit )
716 : : {
717 : : case DAY:
718 : 0 : break;
719 : : case MONTH:
720 : 0 : nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
721 : 0 : break;
722 : : case YEAR:
723 : 0 : nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
724 : 0 : break;
725 : : }
726 [ # # ]: 0 : if( nDayCount/nIntervalDayCount > nMaxMainIncrementCount )
727 : 0 : bAutoMinor = true;
728 : : }
729 [ # # ]: 0 : if( bAutoMinor )
730 : : {
731 : 0 : rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
732 : 0 : rExplicitIncrement.MinorTimeInterval.Number = 1;
733 [ # # ]: 0 : if( nMainIncrementCount > 100 )
734 : 0 : rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
735 : : else
736 : : {
737 [ # # ]: 0 : if( rExplicitIncrement.MajorTimeInterval.Number >= 2 )
738 : : {
739 [ # # ]: 0 : if( !(rExplicitIncrement.MajorTimeInterval.Number%2) )
740 : 0 : rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/2;
741 [ # # ]: 0 : else if( !(rExplicitIncrement.MajorTimeInterval.Number%3) )
742 : 0 : rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/3;
743 [ # # ]: 0 : else if( !(rExplicitIncrement.MajorTimeInterval.Number%5) )
744 : 0 : rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/5;
745 [ # # ]: 0 : else if( rExplicitIncrement.MajorTimeInterval.Number > 50 )
746 : 0 : rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
747 : : }
748 : : else
749 : : {
750 [ # # # # ]: 0 : switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
751 : : {
752 : : case DAY:
753 : 0 : break;
754 : : case MONTH:
755 [ # # ]: 0 : if( rExplicitScale.TimeResolution == DAY )
756 : 0 : rExplicitIncrement.MinorTimeInterval.TimeUnit = DAY;
757 : 0 : break;
758 : : case YEAR:
759 [ # # ]: 0 : if( rExplicitScale.TimeResolution <= MONTH )
760 : 0 : rExplicitIncrement.MinorTimeInterval.TimeUnit = MONTH;
761 : 0 : break;
762 : : }
763 : : }
764 : : }
765 : : }
766 : :
767 : 0 : }
768 : :
769 : : //-----------------------------------------------------------------------------------------
770 : :
771 : 3136 : void ScaleAutomatism::calculateExplicitIncrementAndScaleForLinear(
772 : : ExplicitScaleData& rExplicitScale,
773 : : ExplicitIncrementData& rExplicitIncrement,
774 : : bool bAutoMinimum, bool bAutoMaximum ) const
775 : : {
776 : : // *** STEP 1: initialize the range data ***
777 : :
778 : 3136 : double fSourceMinimum = rExplicitScale.Minimum;
779 : 3136 : double fSourceMaximum = rExplicitScale.Maximum;
780 : :
781 : : // set automatic PostEquidistant to true (maybe scaling dependent?)
782 [ + - ]: 3136 : if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
783 : 3136 : rExplicitIncrement.PostEquidistant = sal_True;
784 : :
785 : : /* If range is invalid (minimum greater than maximum), change one of the
786 : : variable limits to validate the range. In this step, a zero-sized range
787 : : is still allowed. */
788 [ + + ]: 3136 : if( fSourceMinimum > fSourceMaximum )
789 : : {
790 : : // force changing the maximum, if both limits are fixed
791 [ - + ][ # # ]: 2 : if( bAutoMaximum || !bAutoMinimum )
792 : 2 : fSourceMaximum = fSourceMinimum;
793 : : else
794 : 2 : fSourceMinimum = fSourceMaximum;
795 : : }
796 : :
797 : : /* If maximum is zero or negative (and therefore minimum too), minimum and
798 : : maximum will be negated and swapped to make the following algorithms
799 : : easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
800 : : [2,5], and the latter will be swapped back later. The range [0,0] is
801 : : explicitly excluded from swapping (this would result in [-1,0] instead
802 : : of the expected [0,1]). */
803 [ + + ][ - + ]: 3136 : bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
804 [ - + ]: 3136 : if( bSwapAndNegateRange )
805 : : {
806 : 0 : double fTempValue = fSourceMinimum;
807 : 0 : fSourceMinimum = -fSourceMaximum;
808 : 0 : fSourceMaximum = -fTempValue;
809 : 0 : ::std::swap( bAutoMinimum, bAutoMaximum );
810 : : }
811 : :
812 : : // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
813 : :
814 : 3136 : double fTempMinimum = fSourceMinimum;
815 : 3136 : double fTempMaximum = fSourceMaximum;
816 : :
817 : : /* If minimum is variable and greater than 0 (and therefore maximum too),
818 : : means all values are positive (or all values are negative, and the
819 : : range has been swapped above), then: */
820 [ + + ][ + + ]: 3136 : if( bAutoMinimum && (fTempMinimum > 0.0) )
821 : : {
822 : : /* If minimum equals maximum, or if minimum is less than 5/6 of
823 : : maximum, set minimum to 0. */
824 [ + + ][ + - ]: 1627 : if( (fTempMinimum == fTempMaximum) || (fTempMinimum / fTempMaximum < 5.0 / 6.0) )
825 : : {
826 [ + + ]: 3253 : if( m_bExpandWideValuesToZero )
827 : 1626 : fTempMinimum = 0.0;
828 : : }
829 : : /* Else (minimum is greater than or equal to 5/6 of maximum), add half
830 : : of the visible range (expand minimum toward 0) to make the
831 : : 'shorter' data points visible. */
832 : : else
833 : : {
834 [ # # ]: 0 : if( m_bExpandNarrowValuesTowardZero )
835 : 1627 : fTempMinimum -= (fTempMaximum - fTempMinimum) / 2.0;
836 : : }
837 : : }
838 : :
839 : : /* If range is still zero-sized (e.g. when minimum is fixed), add some
840 : : space to a variable limit. */
841 [ + + ]: 3136 : if( fTempMinimum == fTempMaximum )
842 : : {
843 [ - + ][ # # ]: 105 : if( bAutoMaximum || !bAutoMinimum )
844 : : {
845 : : // change 0 to 1, otherwise double the value
846 [ + + ]: 210 : if( fTempMaximum == 0.0 )
847 : 102 : fTempMaximum = 1.0;
848 : : else
849 : 3 : fTempMaximum *= 2.0;
850 : : }
851 : : else
852 : : {
853 : : // change 0 to -1, otherwise halve the value
854 [ # # ]: 0 : if( fTempMinimum == 0.0 )
855 : 0 : fTempMinimum = -1.0;
856 : : else
857 : 105 : fTempMinimum /= 2.0;
858 : : }
859 : : }
860 : :
861 : : // *** STEP 3: calculate main interval size ***
862 : :
863 : : // base value (anchor position of the intervals)
864 [ + - ]: 3136 : if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
865 : : {
866 [ + + ]: 3136 : if( !bAutoMinimum )
867 : 104 : rExplicitIncrement.BaseValue = fTempMinimum;
868 [ + + ]: 3032 : else if( !bAutoMaximum )
869 : 12 : rExplicitIncrement.BaseValue = fTempMaximum;
870 : : else
871 : 3020 : rExplicitIncrement.BaseValue = 0.0;
872 : : }
873 : :
874 : : // calculate automatic interval
875 : 3136 : bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
876 : : /* Restrict number of allowed intervals with user-defined distance to
877 : : MAXIMUM_MANUAL_INCREMENT_COUNT. */
878 : : sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
879 [ + + ]: 3136 : m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
880 : :
881 : 3136 : double fDistanceMagnitude = 0.0;
882 : 3136 : double fDistanceNormalized = 0.0;
883 : 3136 : bool bHasNormalizedDistance = false;
884 : :
885 : : // repeat calculation until number of intervals are valid
886 : 3136 : bool bNeedIteration = true;
887 [ + + ]: 8125 : while( bNeedIteration )
888 : : {
889 [ + + ]: 4989 : if( bAutoDistance )
890 : : {
891 : : // first iteration: calculate interval size from axis limits
892 [ + + ]: 4869 : if( !bHasNormalizedDistance )
893 : : {
894 : : // raw size of an interval
895 : 3016 : double fDistance = (fTempMaximum - fTempMinimum) / nMaxMainIncrementCount;
896 : :
897 : : // if distance of is less than 1e-307, do not do anything
898 [ - + ]: 3016 : if( fDistance <= 1.0e-307 )
899 : : {
900 : 0 : fDistanceNormalized = 1.0;
901 : 0 : fDistanceMagnitude = 1.0e-307;
902 : : }
903 [ - + ]: 3016 : else if ( !rtl::math::isFinite(fDistance) )
904 : : {
905 : : // fdo#43703: Handle values bigger than limits correctly
906 : 0 : fDistanceNormalized = 1.0;
907 : 0 : fDistanceMagnitude = std::numeric_limits<double>::max();
908 : : }
909 : : else
910 : : {
911 : : // distance magnitude (a power of 10)
912 : 3016 : int nExponent = static_cast< int >( ::rtl::math::approxFloor( log10( fDistance ) ) );
913 : 3016 : fDistanceMagnitude = ::rtl::math::pow10Exp( 1.0, nExponent );
914 : :
915 : : // stick normalized distance to a few predefined values
916 : 3016 : fDistanceNormalized = fDistance / fDistanceMagnitude;
917 [ + + ]: 3016 : if( fDistanceNormalized <= 1.0 )
918 : 796 : fDistanceNormalized = 1.0;
919 [ + + ]: 2220 : else if( fDistanceNormalized <= 2.0 )
920 : 49 : fDistanceNormalized = 2.0;
921 [ + + ]: 2171 : else if( fDistanceNormalized <= 5.0 )
922 : 319 : fDistanceNormalized = 5.0;
923 : : else
924 : : {
925 : 1852 : fDistanceNormalized = 1.0;
926 : 1852 : fDistanceMagnitude *= 10;
927 : : }
928 : : }
929 : : // for next iteration: distance is normalized -> use else path to increase distance
930 : 3016 : bHasNormalizedDistance = true;
931 : : }
932 : : // following iterations: increase distance, use only allowed values
933 : : else
934 : : {
935 [ + + ]: 1853 : if( fDistanceNormalized == 1.0 )
936 : 1817 : fDistanceNormalized = 2.0;
937 [ + + ]: 36 : else if( fDistanceNormalized == 2.0 )
938 : 3 : fDistanceNormalized = 5.0;
939 : : else
940 : : {
941 : 33 : fDistanceNormalized = 1.0;
942 : 33 : fDistanceMagnitude *= 10;
943 : : }
944 : : }
945 : :
946 : : // set the resulting distance
947 : 4869 : rExplicitIncrement.Distance = fDistanceNormalized * fDistanceMagnitude;
948 : : }
949 : :
950 : : // *** STEP 4: additional space above or below the data points ***
951 : :
952 : 4989 : double fAxisMinimum = fTempMinimum;
953 : 4989 : double fAxisMaximum = fTempMaximum;
954 : :
955 : : // round to entire multiples of the distance and add additional space
956 [ + + ]: 4989 : if( bAutoMinimum )
957 : : {
958 : : // round to entire multiples of the distance, based on the base value
959 [ + + ]: 4885 : if( m_bExpandBorderToIncrementRhythm )
960 : 4098 : fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );
961 : : // additional space, if source minimum is to near at axis minimum
962 [ + + ]: 4885 : if( m_bExpandIfValuesCloseToBorder )
963 [ + + ][ + + ]: 4002 : if( (fAxisMinimum != 0.0) && ((fAxisMaximum - fSourceMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
964 : 6 : fAxisMinimum -= rExplicitIncrement.Distance;
965 : : }
966 [ + + ]: 4989 : if( bAutoMaximum )
967 : : {
968 : : // round to entire multiples of the distance, based on the base value
969 [ + + ]: 4885 : if( m_bExpandBorderToIncrementRhythm )
970 : 4098 : fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
971 : : // additional space, if source maximum is to near at axis maximum
972 [ + + ]: 4885 : if( m_bExpandIfValuesCloseToBorder )
973 [ + - ][ + + ]: 4002 : if( (fAxisMaximum != 0.0) && ((fSourceMaximum - fAxisMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
974 : 3774 : fAxisMaximum += rExplicitIncrement.Distance;
975 : : }
976 : :
977 : : // set the resulting limits (swap back to negative range if needed)
978 [ - + ]: 4989 : if( bSwapAndNegateRange )
979 : : {
980 : 0 : rExplicitScale.Minimum = -fAxisMaximum;
981 : 0 : rExplicitScale.Maximum = -fAxisMinimum;
982 : : }
983 : : else
984 : : {
985 : 4989 : rExplicitScale.Minimum = fAxisMinimum;
986 : 4989 : rExplicitScale.Maximum = fAxisMaximum;
987 : : }
988 : :
989 : : /* If the number of intervals is too high (e.g. due to invalid fixed
990 : : distance or due to added space above or below data points),
991 : : calculate again with increased distance. */
992 : 4989 : double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
993 : 4989 : bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
994 : : // if manual distance is invalid, trigger automatic calculation
995 [ + + ]: 4989 : if( bNeedIteration )
996 : 1853 : bAutoDistance = true;
997 : : }
998 : :
999 : : //---------------------------------------------------------------
1000 : : //fill explicit sub increment
1001 : 3136 : sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
1002 [ + + ]: 6272 : for( sal_Int32 nN=0; nN<nSubCount; nN++ )
1003 : : {
1004 [ + - ]: 3136 : ExplicitSubIncrement aExplicitSubIncrement;
1005 : 3136 : const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
1006 [ + + ]: 3136 : if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
1007 : : {
1008 : : //scaling dependent
1009 : : //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
1010 : 3024 : aExplicitSubIncrement.IntervalCount = 2;
1011 : : }
1012 : 3136 : lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
1013 [ + - ]: 3136 : if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
1014 : : {
1015 : : //scaling dependent
1016 : 3136 : aExplicitSubIncrement.PostEquidistant = sal_False;
1017 : : }
1018 [ + - ]: 3136 : rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
1019 : : }
1020 : 3136 : }
1021 : :
1022 : : //.............................................................................
1023 : : } //namespace chart
1024 : : //.............................................................................
1025 : :
1026 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|