merge the formfield patch from ooo-build
[ooovba.git] / chart2 / source / view / axes / ScaleAutomatism.cxx
blob57922f113eb307b5b2d36a717f557ecab06c3fd1
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
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"
34 #include "macros.hxx"
35 #include "TickmarkHelper.hxx"
36 #include "AxisHelper.hxx"
37 #include <rtl/math.hxx>
38 #include <tools/debug.hxx>
40 //.............................................................................
41 namespace chart
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;
51 namespace
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;
113 else
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
131 if( bAutoMinimum )
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????
145 else
146 rExplicitScale.Minimum = m_fValueMinimum;
149 // automatic scale maximum
150 if( bAutoMaximum )
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????
164 else
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 );
179 else
181 bIsLogarithm = AxisHelper::isLogarithmic( rExplicitScale.Scaling );
182 if( bIsLogarithm )
183 calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
184 else
185 calculateExplicitIncrementAndScaleForLinear( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
188 // automatic origin
189 if( bAutoOrigin )
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))
253 //scaling dependent
254 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
255 rExplicitSubIncrement.IntervalCount = 2;
257 lcl_ensureMaximumSubIncrementCount( rExplicitSubIncrement.IntervalCount );
258 if(!(rSubIncrement.PostEquidistant>>=rExplicitSubIncrement.PostEquidistant))
260 //scaling dependent
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;
289 if( !xScaling.is() )
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
307 is still allowed. */
308 if( fSourceMinimum > fSourceMaximum )
310 // force changing the maximum, if both limits are fixed
311 if( bAutoMaximum || !bAutoMinimum )
312 fSourceMaximum = fSourceMinimum;
313 else
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 )
356 fTempMinimum = 0.0;
358 /* Else (minimum and maximum are in one increment interval), expand
359 minimum toward 0 to make the 'shorter' data points visible. */
360 else
362 if( m_bExpandNarrowValuesTowardZero )
363 fTempMinimum -= 1.0;
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) )
372 fTempMinimum = 0.0;
373 else
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) )
382 //scaling dependent
383 //@maybe todo is this default also plotter dependent ??
384 if( !bAutoMinimum )
385 rExplicitIncrement.BaseValue = fTempMinimum;
386 else if( !bAutoMaximum )
387 rExplicitIncrement.BaseValue = fTempMaximum;
388 else
389 rExplicitIncrement.BaseValue = 0.0;
392 // calculate automatic interval
393 bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
394 if( bAutoDistance )
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 )
407 if( bAutoDistance )
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 );
416 else
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#
437 if( !bAutoDistance )
439 double fCheck = xInverseScaling->doScaling( fAxisMinimum );
440 if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 )
442 bAutoDistance = true;
443 bHasCalculatedDistance = false;
444 continue;
448 if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
450 fAxisMaximum = TickmarkHelper::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
452 //ensure valid values after scaling #i100995#
453 if( !bAutoDistance )
455 double fCheck = xInverseScaling->doScaling( fAxisMaximum );
456 if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 )
458 bAutoDistance = true;
459 bHasCalculatedDistance = false;
460 continue;
465 // set the resulting limits (swap back to negative range if needed)
466 if( bSwapAndNegateRange )
468 rExplicitScale.Minimum = -fAxisMaximum;
469 rExplicitScale.Maximum = -fAxisMinimum;
471 else
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
483 if( bNeedIteration )
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))
518 //scaling dependent
519 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
520 rExplicitSubIncrement.IntervalCount = 5;
522 lcl_ensureMaximumSubIncrementCount( rExplicitSubIncrement.IntervalCount );
523 if(!(rSubIncrement.PostEquidistant>>=rExplicitSubIncrement.PostEquidistant))
525 //scaling dependent
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
547 is still allowed. */
548 if( fSourceMinimum > fSourceMaximum )
550 // force changing the maximum, if both limits are fixed
551 if( bAutoMaximum || !bAutoMinimum )
552 fSourceMaximum = fSourceMinimum;
553 else
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 )
587 fTempMinimum = 0.0;
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. */
592 else
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 )
607 fTempMaximum = 1.0;
608 else
609 fTempMaximum *= 2.0;
611 else
613 // change 0 to -1, otherwise halve the value
614 if( fTempMinimum == 0.0 )
615 fTempMinimum = -1.0;
616 else
617 fTempMinimum /= 2.0;
621 // *** STEP 3: calculate main interval size ***
623 // base value (anchor position of the intervals)
624 if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
626 if( !bAutoMinimum )
627 rExplicitIncrement.BaseValue = fTempMinimum;
628 else if( !bAutoMaximum )
629 rExplicitIncrement.BaseValue = fTempMaximum;
630 else
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 )
649 if( bAutoDistance )
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;
663 else
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;
677 else
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
687 else
689 if( fDistanceNormalized == 1.0 )
690 fDistanceNormalized = 2.0;
691 else if( fDistanceNormalized == 2.0 )
692 fDistanceNormalized = 5.0;
693 else
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
710 if( bAutoMinimum )
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;
720 if( bAutoMaximum )
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;
737 else
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
749 if( bNeedIteration )
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))
764 //scaling dependent
765 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
766 rExplicitSubIncrement.IntervalCount = 2;
768 lcl_ensureMaximumSubIncrementCount( rExplicitSubIncrement.IntervalCount );
769 if(!(rSubIncrement.PostEquidistant>>=rExplicitSubIncrement.PostEquidistant))
771 //scaling dependent
772 rExplicitSubIncrement.PostEquidistant = sal_False;
777 //.............................................................................
778 } //namespace chart
779 //.............................................................................