1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: ScaleAutomatism.cxx,v $
10 * $Revision: 1.12.24.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_chart2.hxx"
33 #include "ScaleAutomatism.hxx"
35 #include "TickmarkHelper.hxx"
36 #include "AxisHelper.hxx"
37 #include <rtl/math.hxx>
38 #include <tools/debug.hxx>
40 //.............................................................................
43 //.............................................................................
44 using namespace ::com::sun::star
;
45 using namespace ::com::sun::star::chart2
;
47 const sal_Int32 MAXIMUM_MANUAL_INCREMENT_COUNT
= 500;
48 const sal_Int32 MAXIMUM_AUTO_INCREMENT_COUNT
= 10;
49 const sal_Int32 MAXIMUM_SUB_INCREMENT_COUNT
= 100;
54 void lcl_ensureMaximumSubIncrementCount( sal_Int32
& rnSubIntervalCount
)
56 if( rnSubIntervalCount
> MAXIMUM_SUB_INCREMENT_COUNT
)
57 rnSubIntervalCount
= MAXIMUM_SUB_INCREMENT_COUNT
;
60 }//end anonymous namespace
62 ScaleAutomatism::ScaleAutomatism( const ScaleData
& rSourceScale
)
63 : m_aSourceScale( rSourceScale
)
64 , m_fValueMinimum( 0.0 )
65 , m_fValueMaximum( 0.0 )
66 , m_nMaximumAutoMainIncrementCount( MAXIMUM_AUTO_INCREMENT_COUNT
)
67 , m_bExpandBorderToIncrementRhythm( false )
68 , m_bExpandIfValuesCloseToBorder( false )
69 , m_bExpandWideValuesToZero( false )
70 , m_bExpandNarrowValuesTowardZero( false )
72 ::rtl::math::setNan( &m_fValueMinimum
);
73 ::rtl::math::setNan( &m_fValueMaximum
);
75 double fExplicitOrigin
= 0.0;
76 if( m_aSourceScale
.Origin
>>= fExplicitOrigin
)
77 expandValueRange( fExplicitOrigin
, fExplicitOrigin
);
79 ScaleAutomatism::~ScaleAutomatism()
83 void ScaleAutomatism::expandValueRange( double fMinimum
, double fMaximum
)
85 if( (fMinimum
< m_fValueMinimum
) || ::rtl::math::isNan( m_fValueMinimum
) )
86 m_fValueMinimum
= fMinimum
;
87 if( (fMaximum
> m_fValueMaximum
) || ::rtl::math::isNan( m_fValueMaximum
) )
88 m_fValueMaximum
= fMaximum
;
91 void ScaleAutomatism::setAutoScalingOptions(
92 bool bExpandBorderToIncrementRhythm
,
93 bool bExpandIfValuesCloseToBorder
,
94 bool bExpandWideValuesToZero
,
95 bool bExpandNarrowValuesTowardZero
)
97 // if called multiple times, enable an option, if it is set in at least one call
98 m_bExpandBorderToIncrementRhythm
|= bExpandBorderToIncrementRhythm
;
99 m_bExpandIfValuesCloseToBorder
|= bExpandIfValuesCloseToBorder
;
100 m_bExpandWideValuesToZero
|= bExpandWideValuesToZero
;
101 m_bExpandNarrowValuesTowardZero
|= bExpandNarrowValuesTowardZero
;
103 if( m_aSourceScale
.AxisType
==AxisType::PERCENT
)
104 m_bExpandIfValuesCloseToBorder
= false;
107 void ScaleAutomatism::setMaximumAutoMainIncrementCount( sal_Int32 nMaximumAutoMainIncrementCount
)
109 if( nMaximumAutoMainIncrementCount
< 2 )
110 m_nMaximumAutoMainIncrementCount
= 2; //#i82006
111 else if( nMaximumAutoMainIncrementCount
> MAXIMUM_AUTO_INCREMENT_COUNT
)
112 m_nMaximumAutoMainIncrementCount
= MAXIMUM_AUTO_INCREMENT_COUNT
;
114 m_nMaximumAutoMainIncrementCount
= nMaximumAutoMainIncrementCount
;
117 void ScaleAutomatism::calculateExplicitScaleAndIncrement(
118 ExplicitScaleData
& rExplicitScale
, ExplicitIncrementData
& rExplicitIncrement
) const
120 // fill explicit scale
121 rExplicitScale
.Orientation
= m_aSourceScale
.Orientation
;
122 rExplicitScale
.Scaling
= m_aSourceScale
.Scaling
;
123 rExplicitScale
.Breaks
= m_aSourceScale
.Breaks
;
124 rExplicitScale
.AxisType
= m_aSourceScale
.AxisType
;
126 bool bAutoMinimum
= !(m_aSourceScale
.Minimum
>>= rExplicitScale
.Minimum
);
127 bool bAutoMaximum
= !(m_aSourceScale
.Maximum
>>= rExplicitScale
.Maximum
);
128 bool bAutoOrigin
= !(m_aSourceScale
.Origin
>>= rExplicitScale
.Origin
);
130 // automatic scale minimum
133 if( m_aSourceScale
.AxisType
==AxisType::PERCENT
)
135 rExplicitScale
.Minimum
= 0.0;
136 if (!::rtl::math::isNan(m_fValueMinimum
))
138 double fMin
= m_fValueMinimum
/ 100.0;
139 if (rExplicitScale
.Minimum
> fMin
)
140 rExplicitScale
.Minimum
= fMin
;
143 else if( ::rtl::math::isNan( m_fValueMinimum
) )
144 rExplicitScale
.Minimum
= 0.0; //@todo get Minimum from scaling or from plotter????
146 rExplicitScale
.Minimum
= m_fValueMinimum
;
149 // automatic scale maximum
152 if( m_aSourceScale
.AxisType
==AxisType::PERCENT
)
154 rExplicitScale
.Maximum
= 1.0;
155 if (!::rtl::math::isNan(m_fValueMaximum
))
157 double fMax
= m_fValueMaximum
/ 100.0;
158 if (rExplicitScale
.Maximum
< fMax
)
159 rExplicitScale
.Maximum
= fMax
;
162 else if( ::rtl::math::isNan( m_fValueMaximum
) )
163 rExplicitScale
.Maximum
= 10.0; //@todo get Maximum from scaling or from plotter????
165 rExplicitScale
.Maximum
= m_fValueMaximum
;
168 //---------------------------------------------------------------
169 //fill explicit increment
171 rExplicitIncrement
.ShiftedPosition
= (m_aSourceScale
.AxisType
==AxisType::SERIES
) ? true : false;
172 bool bIsLogarithm
= false;
174 //minimum and maximum of the ExplicitScaleData may be changed if allowed
175 if( m_aSourceScale
.AxisType
==AxisType::CATEGORY
|| m_aSourceScale
.AxisType
==AxisType::SERIES
)
177 calculateExplicitIncrementAndScaleForCategory( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
181 bIsLogarithm
= AxisHelper::isLogarithmic( rExplicitScale
.Scaling
);
183 calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
185 calculateExplicitIncrementAndScaleForLinear( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
191 // #i71415# automatic origin for logarithmic axis
192 double fDefaulOrigin
= bIsLogarithm
? 1.0 : 0.0;
194 if( fDefaulOrigin
< rExplicitScale
.Minimum
)
195 fDefaulOrigin
= rExplicitScale
.Minimum
;
196 else if( fDefaulOrigin
> rExplicitScale
.Maximum
)
197 fDefaulOrigin
= rExplicitScale
.Maximum
;
199 rExplicitScale
.Origin
= fDefaulOrigin
;
203 ScaleData
ScaleAutomatism::getScale() const
205 return m_aSourceScale
;
208 // private --------------------------------------------------------------------
210 void ScaleAutomatism::calculateExplicitIncrementAndScaleForCategory(
211 ExplicitScaleData
& rExplicitScale
,
212 ExplicitIncrementData
& rExplicitIncrement
,
213 bool bAutoMinimum
, bool bAutoMaximum
) const
215 // no scaling for categories
216 rExplicitScale
.Scaling
.clear();
218 // ensure that at least one category is visible
219 if( rExplicitScale
.Maximum
<= rExplicitScale
.Minimum
)
220 rExplicitScale
.Maximum
= rExplicitScale
.Minimum
+ 1.0;
222 // default increment settings
223 rExplicitIncrement
.PostEquidistant
= sal_True
; // does not matter anyhow
224 rExplicitIncrement
.Distance
= 1.0; // category axis always have a main increment of 1
225 rExplicitIncrement
.BaseValue
= 0.0; // category axis always have a base of 0
227 // automatic minimum and maximum
228 if( bAutoMinimum
&& m_bExpandBorderToIncrementRhythm
)
229 rExplicitScale
.Minimum
= TickmarkHelper::getMinimumAtIncrement( rExplicitScale
.Minimum
, rExplicitIncrement
);
230 if( bAutoMaximum
&& m_bExpandBorderToIncrementRhythm
)
231 rExplicitScale
.Maximum
= TickmarkHelper::getMaximumAtIncrement( rExplicitScale
.Maximum
, rExplicitIncrement
);
233 //prevent performace killover
234 double fDistanceCount
= ::rtl::math::approxFloor( (rExplicitScale
.Maximum
-rExplicitScale
.Minimum
) / rExplicitIncrement
.Distance
);
235 if( static_cast< sal_Int32
>( fDistanceCount
) > MAXIMUM_MANUAL_INCREMENT_COUNT
)
237 double fMinimumFloor
= ::rtl::math::approxFloor( rExplicitScale
.Minimum
);
238 double fMaximumCeil
= ::rtl::math::approxCeil( rExplicitScale
.Maximum
);
239 rExplicitIncrement
.Distance
= ::rtl::math::approxCeil( (fMaximumCeil
- fMinimumFloor
) / MAXIMUM_MANUAL_INCREMENT_COUNT
);
242 //---------------------------------------------------------------
243 //fill explicit sub increment
244 sal_Int32 nSubCount
= m_aSourceScale
.IncrementData
.SubIncrements
.getLength();
245 rExplicitIncrement
.SubIncrements
.realloc(nSubCount
);
246 for( sal_Int32 nN
=0; nN
<nSubCount
; nN
++ )
248 const SubIncrement
& rSubIncrement
= m_aSourceScale
.IncrementData
.SubIncrements
[nN
];
249 ExplicitSubIncrement
& rExplicitSubIncrement
= rExplicitIncrement
.SubIncrements
[nN
];
251 if(!(rSubIncrement
.IntervalCount
>>=rExplicitSubIncrement
.IntervalCount
))
254 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
255 rExplicitSubIncrement
.IntervalCount
= 2;
257 lcl_ensureMaximumSubIncrementCount( rExplicitSubIncrement
.IntervalCount
);
258 if(!(rSubIncrement
.PostEquidistant
>>=rExplicitSubIncrement
.PostEquidistant
))
261 rExplicitSubIncrement
.PostEquidistant
= sal_False
;
266 //@todo these method should become part of the scaling interface and implementation somehow
267 //@todo problem with outparamters at api
268 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLogarithmic(
269 ExplicitScaleData
& rExplicitScale
,
270 ExplicitIncrementData
& rExplicitIncrement
,
271 bool bAutoMinimum
, bool bAutoMaximum
) const
273 // *** STEP 1: initialize the range data ***
275 const double fInputMinimum
= rExplicitScale
.Minimum
;
276 const double fInputMaximum
= rExplicitScale
.Maximum
;
278 double fSourceMinimum
= rExplicitScale
.Minimum
;
279 double fSourceMaximum
= rExplicitScale
.Maximum
;
281 // set automatic PostEquidistant to true (maybe scaling dependent?)
282 // Note: scaling with PostEquidistant==false is untested and needs review
283 if( !(m_aSourceScale
.IncrementData
.PostEquidistant
>>= rExplicitIncrement
.PostEquidistant
) )
284 rExplicitIncrement
.PostEquidistant
= sal_True
;
286 /* All following scaling code will operate on the logarithms of the source
287 values. In the last step, the original values will be restored. */
288 uno::Reference
< XScaling
> xScaling
= rExplicitScale
.Scaling
;
290 xScaling
.set( AxisHelper::createLogarithmicScaling() );
291 uno::Reference
< XScaling
> xInverseScaling
= xScaling
->getInverseScaling();
293 fSourceMinimum
= xScaling
->doScaling( fSourceMinimum
);
294 if( !::rtl::math::isFinite( fSourceMinimum
) )
295 fSourceMinimum
= 0.0;
296 else if( ::rtl::math::approxEqual( fSourceMinimum
, ::rtl::math::approxFloor( fSourceMinimum
) ) )
297 fSourceMinimum
= ::rtl::math::approxFloor( fSourceMinimum
);
299 fSourceMaximum
= xScaling
->doScaling( fSourceMaximum
);
300 if( !::rtl::math::isFinite( fSourceMaximum
) )
301 fSourceMaximum
= 0.0;
302 else if( ::rtl::math::approxEqual( fSourceMaximum
, ::rtl::math::approxFloor( fSourceMaximum
) ) )
303 fSourceMaximum
= ::rtl::math::approxFloor( fSourceMaximum
);
305 /* If range is invalid (minimum greater than maximum), change one of the
306 variable limits to validate the range. In this step, a zero-sized range
308 if( fSourceMinimum
> fSourceMaximum
)
310 // force changing the maximum, if both limits are fixed
311 if( bAutoMaximum
|| !bAutoMinimum
)
312 fSourceMaximum
= fSourceMinimum
;
314 fSourceMinimum
= fSourceMaximum
;
317 /* If maximum is less than 0 (and therefore minimum too), minimum and
318 maximum will be negated and swapped to make the following algorithms
319 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
320 [2,5], and the latter will be swapped back later. The range [0,0] is
321 explicitly excluded from swapping (this would result in [-1,0] instead
322 of the expected [0,1]). */
323 bool bSwapAndNegateRange
= (fSourceMinimum
< 0.0) && (fSourceMaximum
<= 0.0);
324 if( bSwapAndNegateRange
)
326 double fTempValue
= fSourceMinimum
;
327 fSourceMinimum
= -fSourceMaximum
;
328 fSourceMaximum
= -fTempValue
;
329 ::std::swap( bAutoMinimum
, bAutoMaximum
);
332 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
334 double fTempMinimum
= fSourceMinimum
;
335 double fTempMaximum
= fSourceMaximum
;
337 /* If minimum is variable and greater than 0 (and therefore maximum too),
338 means all original values are greater than 1 (or all values are less
339 than 1, and the range has been swapped above), then: */
340 if( bAutoMinimum
&& (fTempMinimum
> 0.0) )
342 /* If minimum is less than 5 (i.e. original source values less than
343 B^5, B being the base of the scaling), or if minimum and maximum
344 are in different increment intervals (means, if minimum and maximum
345 are not both in the range [B^n,B^(n+1)] for a whole number n), set
346 minimum to 0, which results in B^0=1 on the axis. */
347 double fMinimumFloor
= ::rtl::math::approxFloor( fTempMinimum
);
348 double fMaximumFloor
= ::rtl::math::approxFloor( fTempMaximum
);
349 // handle the exact value B^(n+1) to be in the range [B^n,B^(n+1)]
350 if( ::rtl::math::approxEqual( fTempMaximum
, fMaximumFloor
) )
351 fMaximumFloor
-= 1.0;
353 if( (fMinimumFloor
< 5.0) || (fMinimumFloor
< fMaximumFloor
) )
355 if( m_bExpandWideValuesToZero
)
358 /* Else (minimum and maximum are in one increment interval), expand
359 minimum toward 0 to make the 'shorter' data points visible. */
362 if( m_bExpandNarrowValuesTowardZero
)
367 /* If range is still zero-sized (e.g. when minimum is fixed), set minimum
368 to 0, which makes the axis start/stop at the value 1. */
369 if( fTempMinimum
== fTempMaximum
)
371 if( bAutoMinimum
&& (fTempMaximum
> 0.0) )
374 fTempMaximum
+= 1.0; // always add one interval, even if maximum is fixed
377 // *** STEP 3: calculate main interval size ***
379 // base value (anchor position of the intervals), already scaled
380 if( !(m_aSourceScale
.IncrementData
.BaseValue
>>= rExplicitIncrement
.BaseValue
) )
383 //@maybe todo is this default also plotter dependent ??
385 rExplicitIncrement
.BaseValue
= fTempMinimum
;
386 else if( !bAutoMaximum
)
387 rExplicitIncrement
.BaseValue
= fTempMaximum
;
389 rExplicitIncrement
.BaseValue
= 0.0;
392 // calculate automatic interval
393 bool bAutoDistance
= !(m_aSourceScale
.IncrementData
.Distance
>>= rExplicitIncrement
.Distance
);
395 rExplicitIncrement
.Distance
= 0.0;
397 /* Restrict number of allowed intervals with user-defined distance to
398 MAXIMUM_MANUAL_INCREMENT_COUNT. */
399 sal_Int32 nMaxMainIncrementCount
= bAutoDistance
?
400 m_nMaximumAutoMainIncrementCount
: MAXIMUM_MANUAL_INCREMENT_COUNT
;
402 // repeat calculation until number of intervals are valid
403 bool bNeedIteration
= true;
404 bool bHasCalculatedDistance
= false;
405 while( bNeedIteration
)
409 // first iteration: calculate interval size from axis limits
410 if( !bHasCalculatedDistance
)
412 double fMinimumFloor
= ::rtl::math::approxFloor( fTempMinimum
);
413 double fMaximumCeil
= ::rtl::math::approxCeil( fTempMaximum
);
414 rExplicitIncrement
.Distance
= ::rtl::math::approxCeil( (fMaximumCeil
- fMinimumFloor
) / nMaxMainIncrementCount
);
418 // following iterations: increase distance
419 rExplicitIncrement
.Distance
+= 1.0;
422 // for next iteration: distance calculated -> use else path to increase
423 bHasCalculatedDistance
= true;
426 // *** STEP 4: additional space above or below the data points ***
428 double fAxisMinimum
= fTempMinimum
;
429 double fAxisMaximum
= fTempMaximum
;
431 // round to entire multiples of the distance and add additional space
432 if( bAutoMinimum
&& m_bExpandBorderToIncrementRhythm
)
434 fAxisMinimum
= TickmarkHelper::getMinimumAtIncrement( fAxisMinimum
, rExplicitIncrement
);
436 //ensure valid values after scaling #i100995#
439 double fCheck
= xInverseScaling
->doScaling( fAxisMinimum
);
440 if( !::rtl::math::isFinite( fCheck
) || fCheck
<= 0 )
442 bAutoDistance
= true;
443 bHasCalculatedDistance
= false;
448 if( bAutoMaximum
&& m_bExpandBorderToIncrementRhythm
)
450 fAxisMaximum
= TickmarkHelper::getMaximumAtIncrement( fAxisMaximum
, rExplicitIncrement
);
452 //ensure valid values after scaling #i100995#
455 double fCheck
= xInverseScaling
->doScaling( fAxisMaximum
);
456 if( !::rtl::math::isFinite( fCheck
) || fCheck
<= 0 )
458 bAutoDistance
= true;
459 bHasCalculatedDistance
= false;
465 // set the resulting limits (swap back to negative range if needed)
466 if( bSwapAndNegateRange
)
468 rExplicitScale
.Minimum
= -fAxisMaximum
;
469 rExplicitScale
.Maximum
= -fAxisMinimum
;
473 rExplicitScale
.Minimum
= fAxisMinimum
;
474 rExplicitScale
.Maximum
= fAxisMaximum
;
477 /* If the number of intervals is too high (e.g. due to invalid fixed
478 distance or due to added space above or below data points),
479 calculate again with increased distance. */
480 double fDistanceCount
= ::rtl::math::approxFloor( (fAxisMaximum
- fAxisMinimum
) / rExplicitIncrement
.Distance
);
481 bNeedIteration
= static_cast< sal_Int32
>( fDistanceCount
) > nMaxMainIncrementCount
;
482 // if manual distance is invalid, trigger automatic calculation
484 bAutoDistance
= true;
486 // convert limits back to logarithmic scale
487 rExplicitScale
.Minimum
= xInverseScaling
->doScaling( rExplicitScale
.Minimum
);
488 rExplicitScale
.Maximum
= xInverseScaling
->doScaling( rExplicitScale
.Maximum
);
490 //ensure valid values after scaling #i100995#
491 if( !::rtl::math::isFinite( rExplicitScale
.Minimum
) || rExplicitScale
.Minimum
<= 0)
493 rExplicitScale
.Minimum
= fInputMinimum
;
494 if( !::rtl::math::isFinite( rExplicitScale
.Minimum
) || rExplicitScale
.Minimum
<= 0 )
495 rExplicitScale
.Minimum
= 1.0;
497 if( !::rtl::math::isFinite( rExplicitScale
.Maximum
) || rExplicitScale
.Maximum
<= 0 )
499 rExplicitScale
.Maximum
= fInputMaximum
;
500 if( !::rtl::math::isFinite( rExplicitScale
.Maximum
) || rExplicitScale
.Maximum
<= 0 )
501 rExplicitScale
.Maximum
= 10.0;
503 if( rExplicitScale
.Maximum
< rExplicitScale
.Minimum
)
504 ::std::swap( rExplicitScale
.Maximum
, rExplicitScale
.Minimum
);
507 //---------------------------------------------------------------
508 //fill explicit sub increment
509 sal_Int32 nSubCount
= m_aSourceScale
.IncrementData
.SubIncrements
.getLength();
510 rExplicitIncrement
.SubIncrements
.realloc(nSubCount
);
511 for( sal_Int32 nN
=0; nN
<nSubCount
; nN
++ )
513 const SubIncrement
& rSubIncrement
= m_aSourceScale
.IncrementData
.SubIncrements
[nN
];
514 ExplicitSubIncrement
& rExplicitSubIncrement
= rExplicitIncrement
.SubIncrements
[nN
];
516 if(!(rSubIncrement
.IntervalCount
>>=rExplicitSubIncrement
.IntervalCount
))
519 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
520 rExplicitSubIncrement
.IntervalCount
= 5;
522 lcl_ensureMaximumSubIncrementCount( rExplicitSubIncrement
.IntervalCount
);
523 if(!(rSubIncrement
.PostEquidistant
>>=rExplicitSubIncrement
.PostEquidistant
))
526 rExplicitSubIncrement
.PostEquidistant
= sal_False
;
531 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLinear(
532 ExplicitScaleData
& rExplicitScale
,
533 ExplicitIncrementData
& rExplicitIncrement
,
534 bool bAutoMinimum
, bool bAutoMaximum
) const
536 // *** STEP 1: initialize the range data ***
538 double fSourceMinimum
= rExplicitScale
.Minimum
;
539 double fSourceMaximum
= rExplicitScale
.Maximum
;
541 // set automatic PostEquidistant to true (maybe scaling dependent?)
542 if( !(m_aSourceScale
.IncrementData
.PostEquidistant
>>= rExplicitIncrement
.PostEquidistant
) )
543 rExplicitIncrement
.PostEquidistant
= sal_True
;
545 /* If range is invalid (minimum greater than maximum), change one of the
546 variable limits to validate the range. In this step, a zero-sized range
548 if( fSourceMinimum
> fSourceMaximum
)
550 // force changing the maximum, if both limits are fixed
551 if( bAutoMaximum
|| !bAutoMinimum
)
552 fSourceMaximum
= fSourceMinimum
;
554 fSourceMinimum
= fSourceMaximum
;
557 /* If maximum is zero or negative (and therefore minimum too), minimum and
558 maximum will be negated and swapped to make the following algorithms
559 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
560 [2,5], and the latter will be swapped back later. The range [0,0] is
561 explicitly excluded from swapping (this would result in [-1,0] instead
562 of the expected [0,1]). */
563 bool bSwapAndNegateRange
= (fSourceMinimum
< 0.0) && (fSourceMaximum
<= 0.0);
564 if( bSwapAndNegateRange
)
566 double fTempValue
= fSourceMinimum
;
567 fSourceMinimum
= -fSourceMaximum
;
568 fSourceMaximum
= -fTempValue
;
569 ::std::swap( bAutoMinimum
, bAutoMaximum
);
572 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
574 double fTempMinimum
= fSourceMinimum
;
575 double fTempMaximum
= fSourceMaximum
;
577 /* If minimum is variable and greater than 0 (and therefore maximum too),
578 means all values are positive (or all values are negative, and the
579 range has been swapped above), then: */
580 if( bAutoMinimum
&& (fTempMinimum
> 0.0) )
582 /* If minimum equals maximum, or if minimum is less than 5/6 of
583 maximum, set minimum to 0. */
584 if( (fTempMinimum
== fTempMaximum
) || (fTempMinimum
/ fTempMaximum
< 5.0 / 6.0) )
586 if( m_bExpandWideValuesToZero
)
589 /* Else (minimum is greater than or equal to 5/6 of maximum), add half
590 of the visible range (expand minimum toward 0) to make the
591 'shorter' data points visible. */
594 if( m_bExpandNarrowValuesTowardZero
)
595 fTempMinimum
-= (fTempMaximum
- fTempMinimum
) / 2.0;
599 /* If range is still zero-sized (e.g. when minimum is fixed), add some
600 space to a variable limit. */
601 if( fTempMinimum
== fTempMaximum
)
603 if( bAutoMaximum
|| !bAutoMinimum
)
605 // change 0 to 1, otherwise double the value
606 if( fTempMaximum
== 0.0 )
613 // change 0 to -1, otherwise halve the value
614 if( fTempMinimum
== 0.0 )
621 // *** STEP 3: calculate main interval size ***
623 // base value (anchor position of the intervals)
624 if( !(m_aSourceScale
.IncrementData
.BaseValue
>>= rExplicitIncrement
.BaseValue
) )
627 rExplicitIncrement
.BaseValue
= fTempMinimum
;
628 else if( !bAutoMaximum
)
629 rExplicitIncrement
.BaseValue
= fTempMaximum
;
631 rExplicitIncrement
.BaseValue
= 0.0;
634 // calculate automatic interval
635 bool bAutoDistance
= !(m_aSourceScale
.IncrementData
.Distance
>>= rExplicitIncrement
.Distance
);
636 /* Restrict number of allowed intervals with user-defined distance to
637 MAXIMUM_MANUAL_INCREMENT_COUNT. */
638 sal_Int32 nMaxMainIncrementCount
= bAutoDistance
?
639 m_nMaximumAutoMainIncrementCount
: MAXIMUM_MANUAL_INCREMENT_COUNT
;
641 double fDistanceMagnitude
= 0.0;
642 double fDistanceNormalized
= 0.0;
643 bool bHasNormalizedDistance
= false;
645 // repeat calculation until number of intervals are valid
646 bool bNeedIteration
= true;
647 while( bNeedIteration
)
651 // first iteration: calculate interval size from axis limits
652 if( !bHasNormalizedDistance
)
654 // raw size of an interval
655 double fDistance
= (fTempMaximum
- fTempMinimum
) / nMaxMainIncrementCount
;
657 // if distance of is less than 1e-307, do not do anything
658 if( fDistance
<= 1.0e-307 )
660 fDistanceNormalized
= 1.0;
661 fDistanceMagnitude
= 1.0e-307;
665 // distance magnitude (a power of 10)
666 int nExponent
= static_cast< int >( ::rtl::math::approxFloor( log10( fDistance
) ) );
667 fDistanceMagnitude
= ::rtl::math::pow10Exp( 1.0, nExponent
);
669 // stick normalized distance to a few predefined values
670 fDistanceNormalized
= fDistance
/ fDistanceMagnitude
;
671 if( fDistanceNormalized
<= 1.0 )
672 fDistanceNormalized
= 1.0;
673 else if( fDistanceNormalized
<= 2.0 )
674 fDistanceNormalized
= 2.0;
675 else if( fDistanceNormalized
<= 5.0 )
676 fDistanceNormalized
= 5.0;
679 fDistanceNormalized
= 1.0;
680 fDistanceMagnitude
*= 10;
683 // for next iteration: distance is normalized -> use else path to increase distance
684 bHasNormalizedDistance
= true;
686 // following iterations: increase distance, use only allowed values
689 if( fDistanceNormalized
== 1.0 )
690 fDistanceNormalized
= 2.0;
691 else if( fDistanceNormalized
== 2.0 )
692 fDistanceNormalized
= 5.0;
695 fDistanceNormalized
= 1.0;
696 fDistanceMagnitude
*= 10;
700 // set the resulting distance
701 rExplicitIncrement
.Distance
= fDistanceNormalized
* fDistanceMagnitude
;
704 // *** STEP 4: additional space above or below the data points ***
706 double fAxisMinimum
= fTempMinimum
;
707 double fAxisMaximum
= fTempMaximum
;
709 // round to entire multiples of the distance and add additional space
712 // round to entire multiples of the distance, based on the base value
713 if( m_bExpandBorderToIncrementRhythm
)
714 fAxisMinimum
= TickmarkHelper::getMinimumAtIncrement( fAxisMinimum
, rExplicitIncrement
);
715 // additional space, if source minimum is to near at axis minimum
716 if( m_bExpandIfValuesCloseToBorder
)
717 if( (fAxisMinimum
!= 0.0) && ((fAxisMaximum
- fSourceMinimum
) / (fAxisMaximum
- fAxisMinimum
) > 20.0 / 21.0) )
718 fAxisMinimum
-= rExplicitIncrement
.Distance
;
722 // round to entire multiples of the distance, based on the base value
723 if( m_bExpandBorderToIncrementRhythm
)
724 fAxisMaximum
= TickmarkHelper::getMaximumAtIncrement( fAxisMaximum
, rExplicitIncrement
);
725 // additional space, if source maximum is to near at axis maximum
726 if( m_bExpandIfValuesCloseToBorder
)
727 if( (fAxisMaximum
!= 0.0) && ((fSourceMaximum
- fAxisMinimum
) / (fAxisMaximum
- fAxisMinimum
) > 20.0 / 21.0) )
728 fAxisMaximum
+= rExplicitIncrement
.Distance
;
731 // set the resulting limits (swap back to negative range if needed)
732 if( bSwapAndNegateRange
)
734 rExplicitScale
.Minimum
= -fAxisMaximum
;
735 rExplicitScale
.Maximum
= -fAxisMinimum
;
739 rExplicitScale
.Minimum
= fAxisMinimum
;
740 rExplicitScale
.Maximum
= fAxisMaximum
;
743 /* If the number of intervals is too high (e.g. due to invalid fixed
744 distance or due to added space above or below data points),
745 calculate again with increased distance. */
746 double fDistanceCount
= ::rtl::math::approxFloor( (fAxisMaximum
- fAxisMinimum
) / rExplicitIncrement
.Distance
);
747 bNeedIteration
= static_cast< sal_Int32
>( fDistanceCount
) > nMaxMainIncrementCount
;
748 // if manual distance is invalid, trigger automatic calculation
750 bAutoDistance
= true;
753 //---------------------------------------------------------------
754 //fill explicit sub increment
755 sal_Int32 nSubCount
= m_aSourceScale
.IncrementData
.SubIncrements
.getLength();
756 rExplicitIncrement
.SubIncrements
.realloc(nSubCount
);
757 for( sal_Int32 nN
=0; nN
<nSubCount
; nN
++ )
759 const SubIncrement
& rSubIncrement
= m_aSourceScale
.IncrementData
.SubIncrements
[nN
];
760 ExplicitSubIncrement
& rExplicitSubIncrement
= rExplicitIncrement
.SubIncrements
[nN
];
762 if(!(rSubIncrement
.IntervalCount
>>=rExplicitSubIncrement
.IntervalCount
))
765 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
766 rExplicitSubIncrement
.IntervalCount
= 2;
768 lcl_ensureMaximumSubIncrementCount( rExplicitSubIncrement
.IntervalCount
);
769 if(!(rSubIncrement
.PostEquidistant
>>=rExplicitSubIncrement
.PostEquidistant
))
772 rExplicitSubIncrement
.PostEquidistant
= sal_False
;
777 //.............................................................................
779 //.............................................................................