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