nss: upgrade to release 3.73
[LibreOffice.git] / chart2 / source / view / axes / ScaleAutomatism.cxx
blob7557f9b3d95b8668ca9d309f59a234cabe6098ae
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <ScaleAutomatism.hxx>
21 #include "Tickmarks_Equidistant.hxx"
22 #include <DateHelper.hxx>
23 #include "DateScaling.hxx"
24 #include <AxisHelper.hxx>
25 #include <com/sun/star/chart/TimeUnit.hpp>
26 #include <com/sun/star/chart2/AxisType.hpp>
28 #include <rtl/math.hxx>
29 #include <tools/long.hxx>
30 #include <limits>
32 namespace chart
34 using namespace ::com::sun::star;
35 using namespace ::com::sun::star::chart2;
36 using ::com::sun::star::chart::TimeUnit::DAY;
37 using ::com::sun::star::chart::TimeUnit::MONTH;
38 using ::com::sun::star::chart::TimeUnit::YEAR;
40 const sal_Int32 MAXIMUM_MANUAL_INCREMENT_COUNT = 500;
41 const sal_Int32 MAXIMUM_SUB_INCREMENT_COUNT = 100;
43 static sal_Int32 lcl_getMaximumAutoIncrementCount( sal_Int32 nAxisType )
45 sal_Int32 nMaximumAutoIncrementCount = 10;
46 if( nAxisType==AxisType::DATE )
47 nMaximumAutoIncrementCount = MAXIMUM_MANUAL_INCREMENT_COUNT;
48 return nMaximumAutoIncrementCount;
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 ExplicitScaleData::ExplicitScaleData()
63 : Minimum(0.0)
64 , Maximum(10.0)
65 , Origin(0.0)
66 , Orientation(css::chart2::AxisOrientation_MATHEMATICAL)
67 , Scaling()
68 , AxisType(css::chart2::AxisType::REALNUMBER)
69 , ShiftedCategoryPosition(false)
70 , TimeResolution(css::chart::TimeUnit::DAY)
71 , NullDate(30,12,1899)
75 ExplicitSubIncrement::ExplicitSubIncrement()
76 : IntervalCount(2)
77 , PostEquidistant(true)
81 ExplicitIncrementData::ExplicitIncrementData()
82 : MajorTimeInterval(1,css::chart::TimeUnit::DAY)
83 , MinorTimeInterval(1,css::chart::TimeUnit::DAY)
84 , Distance(1.0)
85 , PostEquidistant(true)
86 , BaseValue(0.0)
87 , SubIncrements()
91 ScaleAutomatism::ScaleAutomatism( const ScaleData& rSourceScale, const Date& rNullDate )
92 : m_aSourceScale( rSourceScale )
93 , m_fValueMinimum( 0.0 )
94 , m_fValueMaximum( 0.0 )
95 , m_nMaximumAutoMainIncrementCount( lcl_getMaximumAutoIncrementCount( rSourceScale.AxisType ) )
96 , m_bExpandBorderToIncrementRhythm( false )
97 , m_bExpandIfValuesCloseToBorder( false )
98 , m_bExpandWideValuesToZero( false )
99 , m_bExpandNarrowValuesTowardZero( false )
100 , m_nTimeResolution(css::chart::TimeUnit::DAY)
101 , m_aNullDate(rNullDate)
103 resetValueRange();
105 double fExplicitOrigin = 0.0;
106 if( m_aSourceScale.Origin >>= fExplicitOrigin )
107 expandValueRange( fExplicitOrigin, fExplicitOrigin);
110 void ScaleAutomatism::resetValueRange( )
112 ::rtl::math::setNan( &m_fValueMinimum );
113 ::rtl::math::setNan( &m_fValueMaximum );
116 void ScaleAutomatism::expandValueRange( double fMinimum, double fMaximum )
118 // if m_fValueMinimum and m_fValueMaximum == 0, it means that they were not determined.
119 // m_fValueMinimum == 0 makes impossible to determine real minimum,
120 // so they need to be reset tdf#96807
121 if( (m_fValueMinimum == 0.0) && (m_fValueMaximum == 0.0) )
122 resetValueRange();
123 if( (fMinimum < m_fValueMinimum) || std::isnan( m_fValueMinimum ) )
124 m_fValueMinimum = fMinimum;
125 if( (fMaximum > m_fValueMaximum) || std::isnan( m_fValueMaximum ) )
126 m_fValueMaximum = fMaximum;
129 void ScaleAutomatism::setAutoScalingOptions(
130 bool bExpandBorderToIncrementRhythm,
131 bool bExpandIfValuesCloseToBorder,
132 bool bExpandWideValuesToZero,
133 bool bExpandNarrowValuesTowardZero )
135 // if called multiple times, enable an option, if it is set in at least one call
136 m_bExpandBorderToIncrementRhythm |= bExpandBorderToIncrementRhythm;
137 m_bExpandIfValuesCloseToBorder |= bExpandIfValuesCloseToBorder;
138 m_bExpandWideValuesToZero |= bExpandWideValuesToZero;
139 m_bExpandNarrowValuesTowardZero |= bExpandNarrowValuesTowardZero;
141 if( m_aSourceScale.AxisType==AxisType::PERCENT )
142 m_bExpandIfValuesCloseToBorder = false;
145 void ScaleAutomatism::setMaximumAutoMainIncrementCount( sal_Int32 nMaximumAutoMainIncrementCount )
147 if( nMaximumAutoMainIncrementCount < 2 )
148 m_nMaximumAutoMainIncrementCount = 2; //#i82006
149 else if( nMaximumAutoMainIncrementCount > lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType ) )
150 m_nMaximumAutoMainIncrementCount = lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType );
151 else
152 m_nMaximumAutoMainIncrementCount = nMaximumAutoMainIncrementCount;
155 void ScaleAutomatism::setAutomaticTimeResolution( sal_Int32 nTimeResolution )
157 m_nTimeResolution = nTimeResolution;
160 void ScaleAutomatism::calculateExplicitScaleAndIncrement(
161 ExplicitScaleData& rExplicitScale, ExplicitIncrementData& rExplicitIncrement ) const
163 // fill explicit scale
164 rExplicitScale.Orientation = m_aSourceScale.Orientation;
165 rExplicitScale.Scaling = m_aSourceScale.Scaling;
166 rExplicitScale.AxisType = m_aSourceScale.AxisType;
167 rExplicitScale.NullDate = m_aNullDate;
169 bool bAutoMinimum = !(m_aSourceScale.Minimum >>= rExplicitScale.Minimum);
170 bool bAutoMaximum = !(m_aSourceScale.Maximum >>= rExplicitScale.Maximum);
171 bool bAutoOrigin = !(m_aSourceScale.Origin >>= rExplicitScale.Origin);
173 // automatic scale minimum
174 if( bAutoMinimum )
176 if( m_aSourceScale.AxisType==AxisType::PERCENT )
177 rExplicitScale.Minimum = 0.0;
178 else if( std::isnan( m_fValueMinimum ) )
180 if( m_aSourceScale.AxisType==AxisType::DATE )
181 rExplicitScale.Minimum = 36526.0; //1.1.2000
182 else
183 rExplicitScale.Minimum = 0.0; //@todo get Minimum from scaling or from plotter????
185 else
186 rExplicitScale.Minimum = m_fValueMinimum;
189 // automatic scale maximum
190 if( bAutoMaximum )
192 if( m_aSourceScale.AxisType==AxisType::PERCENT )
193 rExplicitScale.Maximum = 1.0;
194 else if( std::isnan( m_fValueMaximum ) )
196 if( m_aSourceScale.AxisType==AxisType::DATE )
197 rExplicitScale.Maximum = 40179.0; //1.1.2010
198 else
199 rExplicitScale.Maximum = 10.0; //@todo get Maximum from scaling or from plotter????
201 else
202 rExplicitScale.Maximum = m_fValueMaximum;
205 //fill explicit increment
207 rExplicitScale.ShiftedCategoryPosition = m_aSourceScale.ShiftedCategoryPosition;
208 bool bIsLogarithm = false;
210 //minimum and maximum of the ExplicitScaleData may be changed if allowed
211 if( m_aSourceScale.AxisType==AxisType::DATE )
212 calculateExplicitIncrementAndScaleForDateTimeAxis( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
213 else if( m_aSourceScale.AxisType==AxisType::CATEGORY || m_aSourceScale.AxisType==AxisType::SERIES )
214 calculateExplicitIncrementAndScaleForCategory( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
215 else
217 bIsLogarithm = AxisHelper::isLogarithmic( rExplicitScale.Scaling );
218 if( bIsLogarithm )
219 calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
220 else
221 calculateExplicitIncrementAndScaleForLinear( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
224 // automatic origin
225 if( bAutoOrigin )
227 // #i71415# automatic origin for logarithmic axis
228 double fDefaulOrigin = bIsLogarithm ? 1.0 : 0.0;
230 if( fDefaulOrigin < rExplicitScale.Minimum )
231 fDefaulOrigin = rExplicitScale.Minimum;
232 else if( fDefaulOrigin > rExplicitScale.Maximum )
233 fDefaulOrigin = rExplicitScale.Maximum;
235 rExplicitScale.Origin = fDefaulOrigin;
239 void ScaleAutomatism::calculateExplicitIncrementAndScaleForCategory(
240 ExplicitScaleData& rExplicitScale,
241 ExplicitIncrementData& rExplicitIncrement,
242 bool bAutoMinimum, bool bAutoMaximum ) const
244 // no scaling for categories
245 rExplicitScale.Scaling.clear();
247 if( rExplicitScale.ShiftedCategoryPosition )
248 rExplicitScale.Maximum += 1.0;
250 // ensure that at least one category is visible
251 if( rExplicitScale.Maximum <= rExplicitScale.Minimum )
252 rExplicitScale.Maximum = rExplicitScale.Minimum + 1.0;
254 // default increment settings
255 rExplicitIncrement.PostEquidistant = true; // does not matter anyhow
256 rExplicitIncrement.Distance = 1.0; // category axis always have a main increment of 1
257 rExplicitIncrement.BaseValue = 0.0; // category axis always have a base of 0
259 // automatic minimum and maximum
260 if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
261 rExplicitScale.Minimum = EquidistantTickFactory::getMinimumAtIncrement( rExplicitScale.Minimum, rExplicitIncrement );
262 if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
263 rExplicitScale.Maximum = EquidistantTickFactory::getMaximumAtIncrement( rExplicitScale.Maximum, rExplicitIncrement );
265 //prevent performance killover
266 double fDistanceCount = ::rtl::math::approxFloor( (rExplicitScale.Maximum-rExplicitScale.Minimum) / rExplicitIncrement.Distance );
267 if( static_cast< sal_Int32 >( fDistanceCount ) > MAXIMUM_MANUAL_INCREMENT_COUNT )
269 double fMinimumFloor = ::rtl::math::approxFloor( rExplicitScale.Minimum );
270 double fMaximumCeil = ::rtl::math::approxCeil( rExplicitScale.Maximum );
271 rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / MAXIMUM_MANUAL_INCREMENT_COUNT );
274 //fill explicit sub increment
275 sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
276 for( sal_Int32 nN=0; nN<nSubCount; nN++ )
278 ExplicitSubIncrement aExplicitSubIncrement;
279 const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
280 if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
282 //scaling dependent
283 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
284 aExplicitSubIncrement.IntervalCount = 2;
286 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
287 if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
289 //scaling dependent
290 aExplicitSubIncrement.PostEquidistant = false;
292 rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
296 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLogarithmic(
297 ExplicitScaleData& rExplicitScale,
298 ExplicitIncrementData& rExplicitIncrement,
299 bool bAutoMinimum, bool bAutoMaximum ) const
301 // *** STEP 1: initialize the range data ***
303 const double fInputMinimum = rExplicitScale.Minimum;
304 const double fInputMaximum = rExplicitScale.Maximum;
306 double fSourceMinimum = rExplicitScale.Minimum;
307 double fSourceMaximum = rExplicitScale.Maximum;
309 // set automatic PostEquidistant to true (maybe scaling dependent?)
310 // Note: scaling with PostEquidistant==false is untested and needs review
311 if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
312 rExplicitIncrement.PostEquidistant = true;
314 /* All following scaling code will operate on the logarithms of the source
315 values. In the last step, the original values will be restored. */
316 uno::Reference< XScaling > xScaling = rExplicitScale.Scaling;
317 if( !xScaling.is() )
318 xScaling.set( AxisHelper::createLogarithmicScaling() );
319 uno::Reference< XScaling > xInverseScaling = xScaling->getInverseScaling();
321 fSourceMinimum = xScaling->doScaling( fSourceMinimum );
322 if( !std::isfinite( fSourceMinimum ) )
323 fSourceMinimum = 0.0;
324 else if( ::rtl::math::approxEqual( fSourceMinimum, ::rtl::math::approxFloor( fSourceMinimum ) ) )
325 fSourceMinimum = ::rtl::math::approxFloor( fSourceMinimum );
327 fSourceMaximum = xScaling->doScaling( fSourceMaximum );
328 if( !std::isfinite( fSourceMaximum ) )
329 fSourceMaximum = 0.0;
330 else if( ::rtl::math::approxEqual( fSourceMaximum, ::rtl::math::approxFloor( fSourceMaximum ) ) )
331 fSourceMaximum = ::rtl::math::approxFloor( fSourceMaximum );
333 /* If range is invalid (minimum greater than maximum), change one of the
334 variable limits to validate the range. In this step, a zero-sized range
335 is still allowed. */
336 if( fSourceMinimum > fSourceMaximum )
338 // force changing the maximum, if both limits are fixed
339 if( bAutoMaximum || !bAutoMinimum )
340 fSourceMaximum = fSourceMinimum;
341 else
342 fSourceMinimum = fSourceMaximum;
345 /* If maximum is less than 0 (and therefore minimum too), minimum and
346 maximum will be negated and swapped to make the following algorithms
347 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
348 [2,5], and the latter will be swapped back later. The range [0,0] is
349 explicitly excluded from swapping (this would result in [-1,0] instead
350 of the expected [0,1]). */
351 bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
352 if( bSwapAndNegateRange )
354 double fTempValue = fSourceMinimum;
355 fSourceMinimum = -fSourceMaximum;
356 fSourceMaximum = -fTempValue;
357 std::swap( bAutoMinimum, bAutoMaximum );
360 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
362 double fTempMinimum = fSourceMinimum;
363 double fTempMaximum = fSourceMaximum;
365 /* If minimum is variable and greater than 0 (and therefore maximum too),
366 means all original values are greater than 1 (or all values are less
367 than 1, and the range has been swapped above), then: */
368 if( bAutoMinimum && (fTempMinimum > 0.0) )
370 double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
371 double fMaximumFloor = ::rtl::math::approxFloor( fTempMaximum );
372 // handle the exact value B^(n+1) to be in the range [B^n,B^(n+1)]
373 if( ::rtl::math::approxEqual( fTempMaximum, fMaximumFloor ) )
374 fMaximumFloor -= 1.0;
376 if( fMinimumFloor == fMaximumFloor )
378 /* if minimum and maximum are in one increment interval, expand
379 minimum toward 0 to make the 'shorter' data points visible. */
380 if( m_bExpandNarrowValuesTowardZero )
381 fTempMinimum -= 1.0;
385 /* If range is still zero-sized (e.g. when minimum is fixed), set minimum
386 to 0, which makes the axis start/stop at the value 1. */
387 if( fTempMinimum == fTempMaximum )
389 if( bAutoMinimum && (fTempMaximum > 0.0) )
390 fTempMinimum = 0.0;
391 else
392 fTempMaximum += 1.0; // always add one interval, even if maximum is fixed
395 // *** STEP 3: calculate main interval size ***
397 // base value (anchor position of the intervals), already scaled
398 if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
400 //scaling dependent
401 //@maybe todo is this default also plotter dependent ??
402 if( !bAutoMinimum )
403 rExplicitIncrement.BaseValue = fTempMinimum;
404 else if( !bAutoMaximum )
405 rExplicitIncrement.BaseValue = fTempMaximum;
406 else
407 rExplicitIncrement.BaseValue = 0.0;
410 // calculate automatic interval
411 bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
412 if( bAutoDistance )
413 rExplicitIncrement.Distance = 0.0;
415 /* Restrict number of allowed intervals with user-defined distance to
416 MAXIMUM_MANUAL_INCREMENT_COUNT. */
417 sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
418 m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
420 // repeat calculation until number of intervals are valid
421 bool bNeedIteration = true;
422 bool bHasCalculatedDistance = false;
423 while( bNeedIteration )
425 if( bAutoDistance )
427 // first iteration: calculate interval size from axis limits
428 if( !bHasCalculatedDistance )
430 double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
431 double fMaximumCeil = ::rtl::math::approxCeil( fTempMaximum );
432 rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / nMaxMainIncrementCount );
434 else
436 // following iterations: increase distance
437 rExplicitIncrement.Distance += 1.0;
440 // for next iteration: distance calculated -> use else path to increase
441 bHasCalculatedDistance = true;
444 // *** STEP 4: additional space above or below the data points ***
446 double fAxisMinimum = fTempMinimum;
447 double fAxisMaximum = fTempMaximum;
449 // round to entire multiples of the distance and add additional space
450 if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
452 fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );
454 //ensure valid values after scaling #i100995#
455 if( !bAutoDistance )
457 double fCheck = xInverseScaling->doScaling( fAxisMinimum );
458 if( !std::isfinite( fCheck ) || fCheck <= 0 )
460 bAutoDistance = true;
461 bHasCalculatedDistance = false;
462 continue;
466 if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
468 fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
470 //ensure valid values after scaling #i100995#
471 if( !bAutoDistance )
473 double fCheck = xInverseScaling->doScaling( fAxisMaximum );
474 if( !std::isfinite( fCheck ) || fCheck <= 0 )
476 bAutoDistance = true;
477 bHasCalculatedDistance = false;
478 continue;
483 // set the resulting limits (swap back to negative range if needed)
484 if( bSwapAndNegateRange )
486 rExplicitScale.Minimum = -fAxisMaximum;
487 rExplicitScale.Maximum = -fAxisMinimum;
489 else
491 rExplicitScale.Minimum = fAxisMinimum;
492 rExplicitScale.Maximum = fAxisMaximum;
495 /* If the number of intervals is too high (e.g. due to invalid fixed
496 distance or due to added space above or below data points),
497 calculate again with increased distance. */
498 double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
499 bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
500 // if manual distance is invalid, trigger automatic calculation
501 if( bNeedIteration )
502 bAutoDistance = true;
504 // convert limits back to logarithmic scale
505 rExplicitScale.Minimum = xInverseScaling->doScaling( rExplicitScale.Minimum );
506 rExplicitScale.Maximum = xInverseScaling->doScaling( rExplicitScale.Maximum );
508 //ensure valid values after scaling #i100995#
509 if( !std::isfinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0)
511 rExplicitScale.Minimum = fInputMinimum;
512 if( !std::isfinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0 )
513 rExplicitScale.Minimum = 1.0;
515 if( !std::isfinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
517 rExplicitScale.Maximum= fInputMaximum;
518 if( !std::isfinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
519 rExplicitScale.Maximum = 10.0;
521 if( rExplicitScale.Maximum < rExplicitScale.Minimum )
522 std::swap( rExplicitScale.Maximum, rExplicitScale.Minimum );
525 //fill explicit sub increment
526 sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
527 for( sal_Int32 nN=0; nN<nSubCount; nN++ )
529 ExplicitSubIncrement aExplicitSubIncrement;
530 const SubIncrement& rSubIncrement = m_aSourceScale.IncrementData.SubIncrements[nN];
531 if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
533 //scaling dependent
534 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
535 aExplicitSubIncrement.IntervalCount = 9;
537 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
538 if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
540 //scaling dependent
541 aExplicitSubIncrement.PostEquidistant = false;
543 rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
547 void ScaleAutomatism::calculateExplicitIncrementAndScaleForDateTimeAxis(
548 ExplicitScaleData& rExplicitScale,
549 ExplicitIncrementData& rExplicitIncrement,
550 bool bAutoMinimum, bool bAutoMaximum ) const
552 Date aMinDate(m_aNullDate); aMinDate.AddDays(::rtl::math::approxFloor(rExplicitScale.Minimum));
553 Date aMaxDate(m_aNullDate); aMaxDate.AddDays(::rtl::math::approxFloor(rExplicitScale.Maximum));
554 rExplicitIncrement.PostEquidistant = false;
556 if( aMinDate > aMaxDate )
558 std::swap(aMinDate,aMaxDate);
561 if( !(m_aSourceScale.TimeIncrement.TimeResolution >>= rExplicitScale.TimeResolution) )
562 rExplicitScale.TimeResolution = m_nTimeResolution;
564 rExplicitScale.Scaling = new DateScaling(m_aNullDate,rExplicitScale.TimeResolution,false);
566 // choose min and max suitable to time resolution
567 switch( rExplicitScale.TimeResolution )
569 case DAY:
570 if( rExplicitScale.ShiftedCategoryPosition )
571 ++aMaxDate; //for explicit scales we need one interval more (maximum excluded)
572 break;
573 case MONTH:
574 aMinDate.SetDay(1);
575 aMaxDate.SetDay(1);
576 if( rExplicitScale.ShiftedCategoryPosition )
577 aMaxDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
578 if( DateHelper::IsLessThanOneMonthAway( aMinDate, aMaxDate ) )
580 if( bAutoMaximum || !bAutoMinimum )
581 aMaxDate = DateHelper::GetDateSomeMonthsAway(aMinDate,1);
582 else
583 aMinDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,-1);
585 break;
586 case YEAR:
587 aMinDate.SetDay(1);
588 aMinDate.SetMonth(1);
589 aMaxDate.SetDay(1);
590 aMaxDate.SetMonth(1);
591 if( rExplicitScale.ShiftedCategoryPosition )
592 aMaxDate = DateHelper::GetDateSomeYearsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
593 if( DateHelper::IsLessThanOneYearAway( aMinDate, aMaxDate ) )
595 if( bAutoMaximum || !bAutoMinimum )
596 aMaxDate = DateHelper::GetDateSomeYearsAway(aMinDate,1);
597 else
598 aMinDate = DateHelper::GetDateSomeYearsAway(aMaxDate,-1);
600 break;
603 // set the resulting limits (swap back to negative range if needed)
604 rExplicitScale.Minimum = aMinDate - m_aNullDate;
605 rExplicitScale.Maximum = aMaxDate - m_aNullDate;
607 bool bAutoMajor = !(m_aSourceScale.TimeIncrement.MajorTimeInterval >>= rExplicitIncrement.MajorTimeInterval);
608 bool bAutoMinor = !(m_aSourceScale.TimeIncrement.MinorTimeInterval >>= rExplicitIncrement.MinorTimeInterval);
610 sal_Int32 nMaxMainIncrementCount = bAutoMajor ?
611 m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
612 if( nMaxMainIncrementCount > 1 )
613 nMaxMainIncrementCount--;
615 //choose major time interval:
616 tools::Long nDayCount = aMaxDate - aMinDate;
617 tools::Long nMainIncrementCount = 1;
618 if( !bAutoMajor )
620 tools::Long nIntervalDayCount = rExplicitIncrement.MajorTimeInterval.Number;
621 if( rExplicitIncrement.MajorTimeInterval.TimeUnit < rExplicitScale.TimeResolution )
622 rExplicitIncrement.MajorTimeInterval.TimeUnit = rExplicitScale.TimeResolution;
623 switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
625 case DAY:
626 break;
627 case MONTH:
628 nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
629 break;
630 case YEAR:
631 nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
632 break;
634 nMainIncrementCount = nDayCount/nIntervalDayCount;
635 if( nMainIncrementCount > nMaxMainIncrementCount )
636 bAutoMajor = true;
638 if( bAutoMajor )
640 tools::Long nNumer = 1;
641 tools::Long nIntervalDays = nDayCount / nMaxMainIncrementCount;
642 double nDaysPerInterval = 1.0;
643 if( nIntervalDays>365 || rExplicitScale.TimeResolution==YEAR )
645 rExplicitIncrement.MajorTimeInterval.TimeUnit = YEAR;
646 nDaysPerInterval = 365.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
648 else if( nIntervalDays>31 || rExplicitScale.TimeResolution==MONTH )
650 rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
651 nDaysPerInterval = 31.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
653 else
655 rExplicitIncrement.MajorTimeInterval.TimeUnit = DAY;
656 nDaysPerInterval = 1.0;
659 nNumer = static_cast<sal_Int32>( rtl::math::approxCeil( nIntervalDays/nDaysPerInterval ) );
660 if(nNumer<=0)
661 nNumer=1;
662 if( rExplicitIncrement.MajorTimeInterval.TimeUnit == DAY )
664 if( nNumer>2 && nNumer<7 )
665 nNumer=7;
666 else if( nNumer>7 )
668 rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
669 nDaysPerInterval = 31.0;
670 nNumer = static_cast<sal_Int32>( rtl::math::approxCeil( nIntervalDays/nDaysPerInterval ) );
671 if(nNumer<=0)
672 nNumer=1;
675 rExplicitIncrement.MajorTimeInterval.Number = nNumer;
676 nMainIncrementCount = static_cast<tools::Long>(nDayCount/(nNumer*nDaysPerInterval));
679 //choose minor time interval:
680 if( !bAutoMinor )
682 if( rExplicitIncrement.MinorTimeInterval.TimeUnit > rExplicitIncrement.MajorTimeInterval.TimeUnit )
683 rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
684 tools::Long nIntervalDayCount = rExplicitIncrement.MinorTimeInterval.Number;
685 switch( rExplicitIncrement.MinorTimeInterval.TimeUnit )
687 case DAY:
688 break;
689 case MONTH:
690 nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
691 break;
692 case YEAR:
693 nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
694 break;
696 if( nDayCount/nIntervalDayCount > nMaxMainIncrementCount )
697 bAutoMinor = true;
699 if( !bAutoMinor )
700 return;
702 rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
703 rExplicitIncrement.MinorTimeInterval.Number = 1;
704 if( nMainIncrementCount > 100 )
705 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
706 else
708 if( rExplicitIncrement.MajorTimeInterval.Number >= 2 )
710 if( !(rExplicitIncrement.MajorTimeInterval.Number%2) )
711 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/2;
712 else if( !(rExplicitIncrement.MajorTimeInterval.Number%3) )
713 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/3;
714 else if( !(rExplicitIncrement.MajorTimeInterval.Number%5) )
715 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/5;
716 else if( rExplicitIncrement.MajorTimeInterval.Number > 50 )
717 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
719 else
721 switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
723 case DAY:
724 break;
725 case MONTH:
726 if( rExplicitScale.TimeResolution == DAY )
727 rExplicitIncrement.MinorTimeInterval.TimeUnit = DAY;
728 break;
729 case YEAR:
730 if( rExplicitScale.TimeResolution <= MONTH )
731 rExplicitIncrement.MinorTimeInterval.TimeUnit = MONTH;
732 break;
739 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLinear(
740 ExplicitScaleData& rExplicitScale,
741 ExplicitIncrementData& rExplicitIncrement,
742 bool bAutoMinimum, bool bAutoMaximum ) const
744 // *** STEP 1: initialize the range data ***
746 double fSourceMinimum = rExplicitScale.Minimum;
747 double fSourceMaximum = rExplicitScale.Maximum;
749 // set automatic PostEquidistant to true (maybe scaling dependent?)
750 if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
751 rExplicitIncrement.PostEquidistant = true;
753 /* If range is invalid (minimum greater than maximum), change one of the
754 variable limits to validate the range. In this step, a zero-sized range
755 is still allowed. */
756 if( fSourceMinimum > fSourceMaximum )
758 // force changing the maximum, if both limits are fixed
759 if( bAutoMaximum || !bAutoMinimum )
760 fSourceMaximum = fSourceMinimum;
761 else
762 fSourceMinimum = fSourceMaximum;
765 /* If maximum is zero or negative (and therefore minimum too), minimum and
766 maximum will be negated and swapped to make the following algorithms
767 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
768 [2,5], and the latter will be swapped back later. The range [0,0] is
769 explicitly excluded from swapping (this would result in [-1,0] instead
770 of the expected [0,1]). */
771 bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
772 if( bSwapAndNegateRange )
774 double fTempValue = fSourceMinimum;
775 fSourceMinimum = -fSourceMaximum;
776 fSourceMaximum = -fTempValue;
777 std::swap( bAutoMinimum, bAutoMaximum );
780 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
782 double fTempMinimum = fSourceMinimum;
783 double fTempMaximum = fSourceMaximum;
785 /* If minimum is variable and greater than 0 (and therefore maximum too),
786 means all values are positive (or all values are negative, and the
787 range has been swapped above), then: */
788 if( bAutoMinimum && (fTempMinimum > 0.0) )
790 /* If minimum equals maximum, or if minimum is less than 5/6 of
791 maximum, set minimum to 0. */
792 if( (fTempMinimum == fTempMaximum) || (fTempMinimum / fTempMaximum < 5.0 / 6.0) )
794 if( m_bExpandWideValuesToZero )
795 fTempMinimum = 0.0;
797 /* Else (minimum is greater than or equal to 5/6 of maximum), add half
798 of the visible range (expand minimum toward 0) to make the
799 'shorter' data points visible. */
800 else
802 if( m_bExpandNarrowValuesTowardZero )
803 fTempMinimum -= (fTempMaximum - fTempMinimum) / 2.0;
807 /* If range is still zero-sized (e.g. when minimum is fixed), add some
808 space to a variable limit. */
809 if( fTempMinimum == fTempMaximum )
811 if( bAutoMaximum || !bAutoMinimum )
813 // change 0 to 1, otherwise double the value
814 if( fTempMaximum == 0.0 )
815 fTempMaximum = 1.0;
816 else
817 fTempMaximum *= 2.0;
819 else
821 // change 0 to -1, otherwise halve the value
822 if( fTempMinimum == 0.0 )
823 fTempMinimum = -1.0;
824 else
825 fTempMinimum /= 2.0;
829 // *** STEP 3: calculate main interval size ***
831 // base value (anchor position of the intervals)
832 if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
834 if( !bAutoMinimum )
835 rExplicitIncrement.BaseValue = fTempMinimum;
836 else if( !bAutoMaximum )
837 rExplicitIncrement.BaseValue = fTempMaximum;
838 else
839 rExplicitIncrement.BaseValue = 0.0;
842 // calculate automatic interval
843 bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
844 /* Restrict number of allowed intervals with user-defined distance to
845 MAXIMUM_MANUAL_INCREMENT_COUNT. */
846 sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
847 m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
849 double fDistanceMagnitude = 0.0;
850 double fDistanceNormalized = 0.0;
851 bool bHasNormalizedDistance = false;
853 // repeat calculation until number of intervals are valid
854 bool bNeedIteration = true;
855 while( bNeedIteration )
857 if( bAutoDistance )
859 // first iteration: calculate interval size from axis limits
860 if( !bHasNormalizedDistance )
862 // raw size of an interval
863 double fDistance = (fTempMaximum - fTempMinimum) / nMaxMainIncrementCount;
865 // if distance of is less than 1e-307, do not do anything
866 if( fDistance <= 1.0e-307 )
868 fDistanceNormalized = 1.0;
869 fDistanceMagnitude = 1.0e-307;
871 else if ( !std::isfinite(fDistance) )
873 // fdo#43703: Handle values bigger than limits correctly
874 fDistanceNormalized = 1.0;
875 fDistanceMagnitude = std::numeric_limits<double>::max();
877 else
879 // distance magnitude (a power of 10)
880 int nExponent = static_cast< int >( ::rtl::math::approxFloor( log10( fDistance ) ) );
881 fDistanceMagnitude = ::rtl::math::pow10Exp( 1.0, nExponent );
883 // stick normalized distance to a few predefined values
884 fDistanceNormalized = fDistance / fDistanceMagnitude;
885 if( fDistanceNormalized <= 1.0 )
886 fDistanceNormalized = 1.0;
887 else if( fDistanceNormalized <= 2.0 )
888 fDistanceNormalized = 2.0;
889 else if( fDistanceNormalized <= 5.0 )
890 fDistanceNormalized = 5.0;
891 else
893 fDistanceNormalized = 1.0;
894 fDistanceMagnitude *= 10;
897 // for next iteration: distance is normalized -> use else path to increase distance
898 bHasNormalizedDistance = true;
900 // following iterations: increase distance, use only allowed values
901 else
903 if( fDistanceNormalized == 1.0 )
904 fDistanceNormalized = 2.0;
905 else if( fDistanceNormalized == 2.0 )
906 fDistanceNormalized = 5.0;
907 else
909 fDistanceNormalized = 1.0;
910 fDistanceMagnitude *= 10;
914 // set the resulting distance
915 rExplicitIncrement.Distance = fDistanceNormalized * fDistanceMagnitude;
918 // *** STEP 4: additional space above or below the data points ***
920 double fAxisMinimum = fTempMinimum;
921 double fAxisMaximum = fTempMaximum;
923 // round to entire multiples of the distance and add additional space
924 if( bAutoMinimum )
926 // round to entire multiples of the distance, based on the base value
927 if( m_bExpandBorderToIncrementRhythm )
928 fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );
929 // additional space, if source minimum is to near at axis minimum
930 if( m_bExpandIfValuesCloseToBorder )
931 if( (fAxisMinimum != 0.0) && ((fAxisMaximum - fSourceMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
932 fAxisMinimum -= rExplicitIncrement.Distance;
934 if( bAutoMaximum )
936 // round to entire multiples of the distance, based on the base value
937 if( m_bExpandBorderToIncrementRhythm )
938 fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
939 // additional space, if source maximum is to near at axis maximum
940 if( m_bExpandIfValuesCloseToBorder )
941 if( (fAxisMaximum != 0.0) && ((fSourceMaximum - fAxisMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
942 fAxisMaximum += rExplicitIncrement.Distance;
945 // set the resulting limits (swap back to negative range if needed)
946 if( bSwapAndNegateRange )
948 rExplicitScale.Minimum = -fAxisMaximum;
949 rExplicitScale.Maximum = -fAxisMinimum;
951 else
953 rExplicitScale.Minimum = fAxisMinimum;
954 rExplicitScale.Maximum = fAxisMaximum;
957 /* If the number of intervals is too high (e.g. due to invalid fixed
958 distance or due to added space above or below data points),
959 calculate again with increased distance. */
960 double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
961 bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
962 // if manual distance is invalid, trigger automatic calculation
963 if( bNeedIteration )
964 bAutoDistance = true;
967 //fill explicit sub increment
968 sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
969 for( sal_Int32 nN=0; nN<nSubCount; nN++ )
971 ExplicitSubIncrement aExplicitSubIncrement;
972 const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
973 if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
975 //scaling dependent
976 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
977 aExplicitSubIncrement.IntervalCount = 2;
979 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
980 if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
982 //scaling dependent
983 aExplicitSubIncrement.PostEquidistant = false;
985 rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
989 } //namespace chart
991 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */