1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
;
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()
66 , Orientation(css::chart2::AxisOrientation_MATHEMATICAL
)
68 , AxisType(css::chart2::AxisType::REALNUMBER
)
69 , ShiftedCategoryPosition(false)
70 , TimeResolution(css::chart::TimeUnit::DAY
)
71 , NullDate(30,12,1899)
75 ExplicitSubIncrement::ExplicitSubIncrement()
77 , PostEquidistant(true)
81 ExplicitIncrementData::ExplicitIncrementData()
82 : MajorTimeInterval(1,css::chart::TimeUnit::DAY
)
83 , MinorTimeInterval(1,css::chart::TimeUnit::DAY
)
85 , PostEquidistant(true)
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
)
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) )
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
);
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
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
183 rExplicitScale
.Minimum
= 0.0; //@todo get Minimum from scaling or from plotter????
186 rExplicitScale
.Minimum
= m_fValueMinimum
;
189 // automatic scale maximum
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
199 rExplicitScale
.Maximum
= 10.0; //@todo get Maximum from scaling or from plotter????
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
);
217 bIsLogarithm
= AxisHelper::isLogarithmic( rExplicitScale
.Scaling
);
219 calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
221 calculateExplicitIncrementAndScaleForLinear( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
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
))
283 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
284 aExplicitSubIncrement
.IntervalCount
= 2;
286 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement
.IntervalCount
);
287 if(!(rSubIncrement
.PostEquidistant
>>=aExplicitSubIncrement
.PostEquidistant
))
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
;
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
336 if( fSourceMinimum
> fSourceMaximum
)
338 // force changing the maximum, if both limits are fixed
339 if( bAutoMaximum
|| !bAutoMinimum
)
340 fSourceMaximum
= fSourceMinimum
;
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
)
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) )
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
) )
401 //@maybe todo is this default also plotter dependent ??
403 rExplicitIncrement
.BaseValue
= fTempMinimum
;
404 else if( !bAutoMaximum
)
405 rExplicitIncrement
.BaseValue
= fTempMaximum
;
407 rExplicitIncrement
.BaseValue
= 0.0;
410 // calculate automatic interval
411 bool bAutoDistance
= !(m_aSourceScale
.IncrementData
.Distance
>>= rExplicitIncrement
.Distance
);
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
)
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
);
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#
457 double fCheck
= xInverseScaling
->doScaling( fAxisMinimum
);
458 if( !std::isfinite( fCheck
) || fCheck
<= 0 )
460 bAutoDistance
= true;
461 bHasCalculatedDistance
= false;
466 if( bAutoMaximum
&& m_bExpandBorderToIncrementRhythm
)
468 fAxisMaximum
= EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum
, rExplicitIncrement
);
470 //ensure valid values after scaling #i100995#
473 double fCheck
= xInverseScaling
->doScaling( fAxisMaximum
);
474 if( !std::isfinite( fCheck
) || fCheck
<= 0 )
476 bAutoDistance
= true;
477 bHasCalculatedDistance
= false;
483 // set the resulting limits (swap back to negative range if needed)
484 if( bSwapAndNegateRange
)
486 rExplicitScale
.Minimum
= -fAxisMaximum
;
487 rExplicitScale
.Maximum
= -fAxisMinimum
;
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
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
))
534 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
535 aExplicitSubIncrement
.IntervalCount
= 9;
537 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement
.IntervalCount
);
538 if(!(rSubIncrement
.PostEquidistant
>>=aExplicitSubIncrement
.PostEquidistant
))
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
)
570 if( rExplicitScale
.ShiftedCategoryPosition
)
571 ++aMaxDate
; //for explicit scales we need one interval more (maximum excluded)
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);
583 aMinDate
= DateHelper::GetDateSomeMonthsAway(aMaxDate
,-1);
588 aMinDate
.SetMonth(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);
598 aMinDate
= DateHelper::GetDateSomeYearsAway(aMaxDate
,-1);
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;
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
)
628 nIntervalDayCount
*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
631 nIntervalDayCount
*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
634 nMainIncrementCount
= nDayCount
/nIntervalDayCount
;
635 if( nMainIncrementCount
> nMaxMainIncrementCount
)
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 ...
655 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= DAY
;
656 nDaysPerInterval
= 1.0;
659 nNumer
= static_cast<sal_Int32
>( rtl::math::approxCeil( nIntervalDays
/nDaysPerInterval
) );
662 if( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
== DAY
)
664 if( nNumer
>2 && nNumer
<7 )
668 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= MONTH
;
669 nDaysPerInterval
= 31.0;
670 nNumer
= static_cast<sal_Int32
>( rtl::math::approxCeil( nIntervalDays
/nDaysPerInterval
) );
675 rExplicitIncrement
.MajorTimeInterval
.Number
= nNumer
;
676 nMainIncrementCount
= static_cast<tools::Long
>(nDayCount
/(nNumer
*nDaysPerInterval
));
679 //choose minor time interval:
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
)
690 nIntervalDayCount
*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
693 nIntervalDayCount
*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
696 if( nDayCount
/nIntervalDayCount
> nMaxMainIncrementCount
)
702 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= rExplicitIncrement
.MajorTimeInterval
.TimeUnit
;
703 rExplicitIncrement
.MinorTimeInterval
.Number
= 1;
704 if( nMainIncrementCount
> 100 )
705 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
;
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
;
721 switch( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
)
726 if( rExplicitScale
.TimeResolution
== DAY
)
727 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= DAY
;
730 if( rExplicitScale
.TimeResolution
<= MONTH
)
731 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= MONTH
;
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
756 if( fSourceMinimum
> fSourceMaximum
)
758 // force changing the maximum, if both limits are fixed
759 if( bAutoMaximum
|| !bAutoMinimum
)
760 fSourceMaximum
= fSourceMinimum
;
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
)
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. */
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 )
821 // change 0 to -1, otherwise halve the value
822 if( fTempMinimum
== 0.0 )
829 // *** STEP 3: calculate main interval size ***
831 // base value (anchor position of the intervals)
832 if( !(m_aSourceScale
.IncrementData
.BaseValue
>>= rExplicitIncrement
.BaseValue
) )
835 rExplicitIncrement
.BaseValue
= fTempMinimum
;
836 else if( !bAutoMaximum
)
837 rExplicitIncrement
.BaseValue
= fTempMaximum
;
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
)
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();
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;
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
903 if( fDistanceNormalized
== 1.0 )
904 fDistanceNormalized
= 2.0;
905 else if( fDistanceNormalized
== 2.0 )
906 fDistanceNormalized
= 5.0;
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
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
;
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
;
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
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
))
976 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
977 aExplicitSubIncrement
.IntervalCount
= 2;
979 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement
.IntervalCount
);
980 if(!(rSubIncrement
.PostEquidistant
>>=aExplicitSubIncrement
.PostEquidistant
))
983 aExplicitSubIncrement
.PostEquidistant
= false;
985 rExplicitIncrement
.SubIncrements
.push_back(aExplicitSubIncrement
);
991 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */