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
)
67 , AxisType(css::chart2::AxisType::REALNUMBER
)
68 , m_bShiftedCategoryPosition(false)
69 , TimeResolution(css::chart::TimeUnit::DAY
)
70 , NullDate(30,12,1899)
74 ExplicitSubIncrement::ExplicitSubIncrement()
76 , PostEquidistant(true)
80 ExplicitIncrementData::ExplicitIncrementData()
81 : MajorTimeInterval(1,css::chart::TimeUnit::DAY
)
82 , MinorTimeInterval(1,css::chart::TimeUnit::DAY
)
84 , PostEquidistant(true)
89 ScaleAutomatism::ScaleAutomatism( const ScaleData
& rSourceScale
, const Date
& rNullDate
)
90 : m_aSourceScale( rSourceScale
)
91 , m_fValueMinimum( 0.0 )
92 , m_fValueMaximum( 0.0 )
93 , m_nMaximumAutoMainIncrementCount( lcl_getMaximumAutoIncrementCount( rSourceScale
.AxisType
) )
94 , m_bExpandBorderToIncrementRhythm( false )
95 , m_bExpandIfValuesCloseToBorder( false )
96 , m_bExpandWideValuesToZero( false )
97 , m_bExpandNarrowValuesTowardZero( false )
98 , m_nTimeResolution(css::chart::TimeUnit::DAY
)
99 , m_aNullDate(rNullDate
)
103 double fExplicitOrigin
= 0.0;
104 if( m_aSourceScale
.Origin
>>= fExplicitOrigin
)
105 expandValueRange( fExplicitOrigin
, fExplicitOrigin
);
108 void ScaleAutomatism::resetValueRange( )
110 m_fValueMinimum
= std::numeric_limits
<double>::quiet_NaN();
111 m_fValueMaximum
= std::numeric_limits
<double>::quiet_NaN();
114 void ScaleAutomatism::expandValueRange( double fMinimum
, double fMaximum
)
116 // if m_fValueMinimum and m_fValueMaximum == 0, it means that they were not determined.
117 // m_fValueMinimum == 0 makes impossible to determine real minimum,
118 // so they need to be reset tdf#96807
119 if( (m_fValueMinimum
== 0.0) && (m_fValueMaximum
== 0.0) )
121 if( (fMinimum
< m_fValueMinimum
) || std::isnan( m_fValueMinimum
) )
122 m_fValueMinimum
= fMinimum
;
123 if( (fMaximum
> m_fValueMaximum
) || std::isnan( m_fValueMaximum
) )
124 m_fValueMaximum
= fMaximum
;
127 void ScaleAutomatism::setAutoScalingOptions(
128 bool bExpandBorderToIncrementRhythm
,
129 bool bExpandIfValuesCloseToBorder
,
130 bool bExpandWideValuesToZero
,
131 bool bExpandNarrowValuesTowardZero
)
133 // if called multiple times, enable an option, if it is set in at least one call
134 m_bExpandBorderToIncrementRhythm
|= bExpandBorderToIncrementRhythm
;
135 m_bExpandIfValuesCloseToBorder
|= bExpandIfValuesCloseToBorder
;
136 m_bExpandWideValuesToZero
|= bExpandWideValuesToZero
;
137 m_bExpandNarrowValuesTowardZero
|= bExpandNarrowValuesTowardZero
;
139 if( m_aSourceScale
.AxisType
==AxisType::PERCENT
)
140 m_bExpandIfValuesCloseToBorder
= false;
143 void ScaleAutomatism::setMaximumAutoMainIncrementCount( sal_Int32 nMaximumAutoMainIncrementCount
)
145 if( nMaximumAutoMainIncrementCount
< 2 )
146 m_nMaximumAutoMainIncrementCount
= 2; //#i82006
147 else if( nMaximumAutoMainIncrementCount
> lcl_getMaximumAutoIncrementCount( m_aSourceScale
.AxisType
) )
148 m_nMaximumAutoMainIncrementCount
= lcl_getMaximumAutoIncrementCount( m_aSourceScale
.AxisType
);
150 m_nMaximumAutoMainIncrementCount
= nMaximumAutoMainIncrementCount
;
153 void ScaleAutomatism::setAutomaticTimeResolution( sal_Int32 nTimeResolution
)
155 m_nTimeResolution
= nTimeResolution
;
158 void ScaleAutomatism::calculateExplicitScaleAndIncrement(
159 ExplicitScaleData
& rExplicitScale
, ExplicitIncrementData
& rExplicitIncrement
) const
161 // fill explicit scale
162 rExplicitScale
.Orientation
= m_aSourceScale
.Orientation
;
163 rExplicitScale
.Scaling
= m_aSourceScale
.Scaling
;
164 rExplicitScale
.AxisType
= m_aSourceScale
.AxisType
;
165 rExplicitScale
.NullDate
= m_aNullDate
;
167 bool bAutoMinimum
= !(m_aSourceScale
.Minimum
>>= rExplicitScale
.Minimum
);
168 bool bAutoMaximum
= !(m_aSourceScale
.Maximum
>>= rExplicitScale
.Maximum
);
169 bool bAutoOrigin
= !(m_aSourceScale
.Origin
>>= rExplicitScale
.Origin
);
171 // automatic scale minimum
174 if( m_aSourceScale
.AxisType
==AxisType::PERCENT
)
175 rExplicitScale
.Minimum
= 0.0;
176 else if( std::isnan( m_fValueMinimum
) )
178 if( m_aSourceScale
.AxisType
==AxisType::DATE
)
179 rExplicitScale
.Minimum
= 36526.0; //1.1.2000
181 rExplicitScale
.Minimum
= 0.0; //@todo get Minimum from scaling or from plotter????
184 rExplicitScale
.Minimum
= m_fValueMinimum
;
187 // automatic scale maximum
190 if( m_aSourceScale
.AxisType
==AxisType::PERCENT
)
191 rExplicitScale
.Maximum
= 1.0;
192 else if( std::isnan( m_fValueMaximum
) )
194 if( m_aSourceScale
.AxisType
==AxisType::DATE
)
195 rExplicitScale
.Maximum
= 40179.0; //1.1.2010
197 rExplicitScale
.Maximum
= 10.0; //@todo get Maximum from scaling or from plotter????
200 rExplicitScale
.Maximum
= m_fValueMaximum
;
203 //fill explicit increment
205 rExplicitScale
.m_bShiftedCategoryPosition
= m_aSourceScale
.ShiftedCategoryPosition
;
206 bool bIsLogarithm
= false;
208 //minimum and maximum of the ExplicitScaleData may be changed if allowed
209 if( m_aSourceScale
.AxisType
==AxisType::DATE
)
210 calculateExplicitIncrementAndScaleForDateTimeAxis( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
211 else if( m_aSourceScale
.AxisType
==AxisType::CATEGORY
|| m_aSourceScale
.AxisType
==AxisType::SERIES
)
212 calculateExplicitIncrementAndScaleForCategory( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
215 bIsLogarithm
= AxisHelper::isLogarithmic( rExplicitScale
.Scaling
);
217 calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
219 calculateExplicitIncrementAndScaleForLinear( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
225 // #i71415# automatic origin for logarithmic axis
226 double fDefaulOrigin
= bIsLogarithm
? 1.0 : 0.0;
228 if( fDefaulOrigin
< rExplicitScale
.Minimum
)
229 fDefaulOrigin
= rExplicitScale
.Minimum
;
230 else if( fDefaulOrigin
> rExplicitScale
.Maximum
)
231 fDefaulOrigin
= rExplicitScale
.Maximum
;
233 rExplicitScale
.Origin
= fDefaulOrigin
;
237 void ScaleAutomatism::calculateExplicitIncrementAndScaleForCategory(
238 ExplicitScaleData
& rExplicitScale
,
239 ExplicitIncrementData
& rExplicitIncrement
,
240 bool bAutoMinimum
, bool bAutoMaximum
) const
242 // no scaling for categories
243 rExplicitScale
.Scaling
.clear();
245 if( rExplicitScale
.m_bShiftedCategoryPosition
)
246 rExplicitScale
.Maximum
+= 1.0;
248 // ensure that at least one category is visible
249 if( rExplicitScale
.Maximum
<= rExplicitScale
.Minimum
)
250 rExplicitScale
.Maximum
= rExplicitScale
.Minimum
+ 1.0;
252 // default increment settings
253 rExplicitIncrement
.PostEquidistant
= true; // does not matter anyhow
254 rExplicitIncrement
.Distance
= 1.0; // category axis always have a main increment of 1
255 rExplicitIncrement
.BaseValue
= 0.0; // category axis always have a base of 0
257 // automatic minimum and maximum
258 if( bAutoMinimum
&& m_bExpandBorderToIncrementRhythm
)
259 rExplicitScale
.Minimum
= EquidistantTickFactory::getMinimumAtIncrement( rExplicitScale
.Minimum
, rExplicitIncrement
);
260 if( bAutoMaximum
&& m_bExpandBorderToIncrementRhythm
)
261 rExplicitScale
.Maximum
= EquidistantTickFactory::getMaximumAtIncrement( rExplicitScale
.Maximum
, rExplicitIncrement
);
263 //prevent performance killover
264 double fDistanceCount
= ::rtl::math::approxFloor( (rExplicitScale
.Maximum
-rExplicitScale
.Minimum
) / rExplicitIncrement
.Distance
);
265 if( static_cast< sal_Int32
>( fDistanceCount
) > MAXIMUM_MANUAL_INCREMENT_COUNT
)
267 double fMinimumFloor
= ::rtl::math::approxFloor( rExplicitScale
.Minimum
);
268 double fMaximumCeil
= ::rtl::math::approxCeil( rExplicitScale
.Maximum
);
269 rExplicitIncrement
.Distance
= ::rtl::math::approxCeil( (fMaximumCeil
- fMinimumFloor
) / MAXIMUM_MANUAL_INCREMENT_COUNT
);
272 //fill explicit sub increment
273 sal_Int32 nSubCount
= m_aSourceScale
.IncrementData
.SubIncrements
.getLength();
274 for( sal_Int32 nN
=0; nN
<nSubCount
; nN
++ )
276 ExplicitSubIncrement aExplicitSubIncrement
;
277 const SubIncrement
& rSubIncrement
= m_aSourceScale
.IncrementData
.SubIncrements
[nN
];
278 if(!(rSubIncrement
.IntervalCount
>>=aExplicitSubIncrement
.IntervalCount
))
281 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
282 aExplicitSubIncrement
.IntervalCount
= 2;
284 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement
.IntervalCount
);
285 if(!(rSubIncrement
.PostEquidistant
>>=aExplicitSubIncrement
.PostEquidistant
))
288 aExplicitSubIncrement
.PostEquidistant
= false;
290 rExplicitIncrement
.SubIncrements
.push_back(aExplicitSubIncrement
);
294 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLogarithmic(
295 ExplicitScaleData
& rExplicitScale
,
296 ExplicitIncrementData
& rExplicitIncrement
,
297 bool bAutoMinimum
, bool bAutoMaximum
) const
299 // *** STEP 1: initialize the range data ***
301 const double fInputMinimum
= rExplicitScale
.Minimum
;
302 const double fInputMaximum
= rExplicitScale
.Maximum
;
304 double fSourceMinimum
= rExplicitScale
.Minimum
;
305 double fSourceMaximum
= rExplicitScale
.Maximum
;
307 // set automatic PostEquidistant to true (maybe scaling dependent?)
308 // Note: scaling with PostEquidistant==false is untested and needs review
309 if( !(m_aSourceScale
.IncrementData
.PostEquidistant
>>= rExplicitIncrement
.PostEquidistant
) )
310 rExplicitIncrement
.PostEquidistant
= true;
312 /* All following scaling code will operate on the logarithms of the source
313 values. In the last step, the original values will be restored. */
314 uno::Reference
< XScaling
> xScaling
= rExplicitScale
.Scaling
;
316 xScaling
.set( AxisHelper::createLogarithmicScaling() );
317 uno::Reference
< XScaling
> xInverseScaling
= xScaling
->getInverseScaling();
319 fSourceMinimum
= xScaling
->doScaling( fSourceMinimum
);
320 if( !std::isfinite( fSourceMinimum
) )
321 fSourceMinimum
= 0.0;
322 else if( ::rtl::math::approxEqual( fSourceMinimum
, ::rtl::math::approxFloor( fSourceMinimum
) ) )
323 fSourceMinimum
= ::rtl::math::approxFloor( fSourceMinimum
);
325 fSourceMaximum
= xScaling
->doScaling( fSourceMaximum
);
326 if( !std::isfinite( fSourceMaximum
) )
327 fSourceMaximum
= 0.0;
328 else if( ::rtl::math::approxEqual( fSourceMaximum
, ::rtl::math::approxFloor( fSourceMaximum
) ) )
329 fSourceMaximum
= ::rtl::math::approxFloor( fSourceMaximum
);
331 /* If range is invalid (minimum greater than maximum), change one of the
332 variable limits to validate the range. In this step, a zero-sized range
334 if( fSourceMinimum
> fSourceMaximum
)
336 // force changing the maximum, if both limits are fixed
337 if( bAutoMaximum
|| !bAutoMinimum
)
338 fSourceMaximum
= fSourceMinimum
;
340 fSourceMinimum
= fSourceMaximum
;
343 /* If maximum is less than 0 (and therefore minimum too), minimum and
344 maximum will be negated and swapped to make the following algorithms
345 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
346 [2,5], and the latter will be swapped back later. The range [0,0] is
347 explicitly excluded from swapping (this would result in [-1,0] instead
348 of the expected [0,1]). */
349 bool bSwapAndNegateRange
= (fSourceMinimum
< 0.0) && (fSourceMaximum
<= 0.0);
350 if( bSwapAndNegateRange
)
352 double fTempValue
= fSourceMinimum
;
353 fSourceMinimum
= -fSourceMaximum
;
354 fSourceMaximum
= -fTempValue
;
355 std::swap( bAutoMinimum
, bAutoMaximum
);
358 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
360 double fTempMinimum
= fSourceMinimum
;
361 double fTempMaximum
= fSourceMaximum
;
363 /* If minimum is variable and greater than 0 (and therefore maximum too),
364 means all original values are greater than 1 (or all values are less
365 than 1, and the range has been swapped above), then: */
366 if( bAutoMinimum
&& (fTempMinimum
> 0.0) )
368 double fMinimumFloor
= ::rtl::math::approxFloor( fTempMinimum
);
369 double fMaximumFloor
= ::rtl::math::approxFloor( fTempMaximum
);
370 // handle the exact value B^(n+1) to be in the range [B^n,B^(n+1)]
371 if( ::rtl::math::approxEqual( fTempMaximum
, fMaximumFloor
) )
372 fMaximumFloor
-= 1.0;
374 if( fMinimumFloor
== fMaximumFloor
)
376 /* if minimum and maximum are in one increment interval, expand
377 minimum toward 0 to make the 'shorter' data points visible. */
378 if( m_bExpandNarrowValuesTowardZero
)
383 /* If range is still zero-sized (e.g. when minimum is fixed), set minimum
384 to 0, which makes the axis start/stop at the value 1. */
385 if( fTempMinimum
== fTempMaximum
)
387 if( bAutoMinimum
&& (fTempMaximum
> 0.0) )
390 fTempMaximum
+= 1.0; // always add one interval, even if maximum is fixed
393 // *** STEP 3: calculate main interval size ***
395 // base value (anchor position of the intervals), already scaled
396 if( !(m_aSourceScale
.IncrementData
.BaseValue
>>= rExplicitIncrement
.BaseValue
) )
399 //@maybe todo is this default also plotter dependent ??
401 rExplicitIncrement
.BaseValue
= fTempMinimum
;
402 else if( !bAutoMaximum
)
403 rExplicitIncrement
.BaseValue
= fTempMaximum
;
405 rExplicitIncrement
.BaseValue
= 0.0;
408 // calculate automatic interval
409 bool bAutoDistance
= !(m_aSourceScale
.IncrementData
.Distance
>>= rExplicitIncrement
.Distance
);
411 rExplicitIncrement
.Distance
= 0.0;
413 /* Restrict number of allowed intervals with user-defined distance to
414 MAXIMUM_MANUAL_INCREMENT_COUNT. */
415 sal_Int32 nMaxMainIncrementCount
= bAutoDistance
?
416 m_nMaximumAutoMainIncrementCount
: MAXIMUM_MANUAL_INCREMENT_COUNT
;
418 // repeat calculation until number of intervals are valid
419 bool bNeedIteration
= true;
420 bool bHasCalculatedDistance
= false;
421 while( bNeedIteration
)
425 // first iteration: calculate interval size from axis limits
426 if( !bHasCalculatedDistance
)
428 double fMinimumFloor
= ::rtl::math::approxFloor( fTempMinimum
);
429 double fMaximumCeil
= ::rtl::math::approxCeil( fTempMaximum
);
430 rExplicitIncrement
.Distance
= ::rtl::math::approxCeil( (fMaximumCeil
- fMinimumFloor
) / nMaxMainIncrementCount
);
434 // following iterations: increase distance
435 rExplicitIncrement
.Distance
+= 1.0;
438 // for next iteration: distance calculated -> use else path to increase
439 bHasCalculatedDistance
= true;
442 // *** STEP 4: additional space above or below the data points ***
444 double fAxisMinimum
= fTempMinimum
;
445 double fAxisMaximum
= fTempMaximum
;
447 // round to entire multiples of the distance and add additional space
448 if( bAutoMinimum
&& m_bExpandBorderToIncrementRhythm
)
450 fAxisMinimum
= EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum
, rExplicitIncrement
);
452 //ensure valid values after scaling #i100995#
455 double fCheck
= xInverseScaling
->doScaling( fAxisMinimum
);
456 if( !std::isfinite( fCheck
) || fCheck
<= 0 )
458 bAutoDistance
= true;
459 bHasCalculatedDistance
= false;
464 if( bAutoMaximum
&& m_bExpandBorderToIncrementRhythm
)
466 fAxisMaximum
= EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum
, rExplicitIncrement
);
468 //ensure valid values after scaling #i100995#
471 double fCheck
= xInverseScaling
->doScaling( fAxisMaximum
);
472 if( !std::isfinite( fCheck
) || fCheck
<= 0 )
474 bAutoDistance
= true;
475 bHasCalculatedDistance
= false;
481 // set the resulting limits (swap back to negative range if needed)
482 if( bSwapAndNegateRange
)
484 rExplicitScale
.Minimum
= -fAxisMaximum
;
485 rExplicitScale
.Maximum
= -fAxisMinimum
;
489 rExplicitScale
.Minimum
= fAxisMinimum
;
490 rExplicitScale
.Maximum
= fAxisMaximum
;
493 /* If the number of intervals is too high (e.g. due to invalid fixed
494 distance or due to added space above or below data points),
495 calculate again with increased distance. */
496 double fDistanceCount
= ::rtl::math::approxFloor( (fAxisMaximum
- fAxisMinimum
) / rExplicitIncrement
.Distance
);
497 bNeedIteration
= static_cast< sal_Int32
>( fDistanceCount
) > nMaxMainIncrementCount
;
498 // if manual distance is invalid, trigger automatic calculation
500 bAutoDistance
= true;
502 // convert limits back to logarithmic scale
503 rExplicitScale
.Minimum
= xInverseScaling
->doScaling( rExplicitScale
.Minimum
);
504 rExplicitScale
.Maximum
= xInverseScaling
->doScaling( rExplicitScale
.Maximum
);
506 //ensure valid values after scaling #i100995#
507 if( !std::isfinite( rExplicitScale
.Minimum
) || rExplicitScale
.Minimum
<= 0)
509 rExplicitScale
.Minimum
= fInputMinimum
;
510 if( !std::isfinite( rExplicitScale
.Minimum
) || rExplicitScale
.Minimum
<= 0 )
511 rExplicitScale
.Minimum
= 1.0;
513 if( !std::isfinite( rExplicitScale
.Maximum
) || rExplicitScale
.Maximum
<= 0 )
515 rExplicitScale
.Maximum
= fInputMaximum
;
516 if( !std::isfinite( rExplicitScale
.Maximum
) || rExplicitScale
.Maximum
<= 0 )
517 rExplicitScale
.Maximum
= 10.0;
519 if( rExplicitScale
.Maximum
< rExplicitScale
.Minimum
)
520 std::swap( rExplicitScale
.Maximum
, rExplicitScale
.Minimum
);
523 //fill explicit sub increment
524 sal_Int32 nSubCount
= m_aSourceScale
.IncrementData
.SubIncrements
.getLength();
525 for( sal_Int32 nN
=0; nN
<nSubCount
; nN
++ )
527 ExplicitSubIncrement aExplicitSubIncrement
;
528 const SubIncrement
& rSubIncrement
= m_aSourceScale
.IncrementData
.SubIncrements
[nN
];
529 if(!(rSubIncrement
.IntervalCount
>>=aExplicitSubIncrement
.IntervalCount
))
532 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
533 aExplicitSubIncrement
.IntervalCount
= 9;
535 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement
.IntervalCount
);
536 if(!(rSubIncrement
.PostEquidistant
>>=aExplicitSubIncrement
.PostEquidistant
))
539 aExplicitSubIncrement
.PostEquidistant
= false;
541 rExplicitIncrement
.SubIncrements
.push_back(aExplicitSubIncrement
);
545 void ScaleAutomatism::calculateExplicitIncrementAndScaleForDateTimeAxis(
546 ExplicitScaleData
& rExplicitScale
,
547 ExplicitIncrementData
& rExplicitIncrement
,
548 bool bAutoMinimum
, bool bAutoMaximum
) const
550 Date
aMinDate(m_aNullDate
); aMinDate
.AddDays(::rtl::math::approxFloor(rExplicitScale
.Minimum
));
551 Date
aMaxDate(m_aNullDate
); aMaxDate
.AddDays(::rtl::math::approxFloor(rExplicitScale
.Maximum
));
552 rExplicitIncrement
.PostEquidistant
= false;
554 if( aMinDate
> aMaxDate
)
556 std::swap(aMinDate
,aMaxDate
);
559 if( !(m_aSourceScale
.TimeIncrement
.TimeResolution
>>= rExplicitScale
.TimeResolution
) )
560 rExplicitScale
.TimeResolution
= m_nTimeResolution
;
562 rExplicitScale
.Scaling
= new DateScaling(m_aNullDate
,rExplicitScale
.TimeResolution
,false);
564 // choose min and max suitable to time resolution
565 switch( rExplicitScale
.TimeResolution
)
568 if( rExplicitScale
.m_bShiftedCategoryPosition
)
569 ++aMaxDate
; //for explicit scales we need one interval more (maximum excluded)
574 if( rExplicitScale
.m_bShiftedCategoryPosition
)
575 aMaxDate
= DateHelper::GetDateSomeMonthsAway(aMaxDate
,1);//for explicit scales we need one interval more (maximum excluded)
576 if( DateHelper::IsLessThanOneMonthAway( aMinDate
, aMaxDate
) )
578 if( bAutoMaximum
|| !bAutoMinimum
)
579 aMaxDate
= DateHelper::GetDateSomeMonthsAway(aMinDate
,1);
581 aMinDate
= DateHelper::GetDateSomeMonthsAway(aMaxDate
,-1);
586 aMinDate
.SetMonth(1);
588 aMaxDate
.SetMonth(1);
589 if( rExplicitScale
.m_bShiftedCategoryPosition
)
590 aMaxDate
= DateHelper::GetDateSomeYearsAway(aMaxDate
,1);//for explicit scales we need one interval more (maximum excluded)
591 if( DateHelper::IsLessThanOneYearAway( aMinDate
, aMaxDate
) )
593 if( bAutoMaximum
|| !bAutoMinimum
)
594 aMaxDate
= DateHelper::GetDateSomeYearsAway(aMinDate
,1);
596 aMinDate
= DateHelper::GetDateSomeYearsAway(aMaxDate
,-1);
601 // set the resulting limits (swap back to negative range if needed)
602 rExplicitScale
.Minimum
= aMinDate
- m_aNullDate
;
603 rExplicitScale
.Maximum
= aMaxDate
- m_aNullDate
;
605 bool bAutoMajor
= !(m_aSourceScale
.TimeIncrement
.MajorTimeInterval
>>= rExplicitIncrement
.MajorTimeInterval
);
606 bool bAutoMinor
= !(m_aSourceScale
.TimeIncrement
.MinorTimeInterval
>>= rExplicitIncrement
.MinorTimeInterval
);
608 sal_Int32 nMaxMainIncrementCount
= bAutoMajor
?
609 m_nMaximumAutoMainIncrementCount
: MAXIMUM_MANUAL_INCREMENT_COUNT
;
610 if( nMaxMainIncrementCount
> 1 )
611 nMaxMainIncrementCount
--;
613 //choose major time interval:
614 tools::Long nDayCount
= aMaxDate
- aMinDate
;
615 tools::Long nMainIncrementCount
= 1;
618 tools::Long nIntervalDayCount
= rExplicitIncrement
.MajorTimeInterval
.Number
;
619 if( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
< rExplicitScale
.TimeResolution
)
620 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= rExplicitScale
.TimeResolution
;
621 switch( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
)
626 nIntervalDayCount
*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
629 nIntervalDayCount
*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
632 nMainIncrementCount
= nDayCount
/nIntervalDayCount
;
633 if( nMainIncrementCount
> nMaxMainIncrementCount
)
638 tools::Long nNumer
= 1;
639 tools::Long nIntervalDays
= nDayCount
/ nMaxMainIncrementCount
;
640 double nDaysPerInterval
= 1.0;
641 if( nIntervalDays
>365 || rExplicitScale
.TimeResolution
==YEAR
)
643 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= YEAR
;
644 nDaysPerInterval
= 365.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
646 else if( nIntervalDays
>31 || rExplicitScale
.TimeResolution
==MONTH
)
648 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= MONTH
;
649 nDaysPerInterval
= 31.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
653 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= DAY
;
654 nDaysPerInterval
= 1.0;
657 nNumer
= static_cast<sal_Int32
>( rtl::math::approxFloor( nIntervalDays
/nDaysPerInterval
) );
660 if( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
== DAY
)
662 if( nNumer
>2 && nNumer
<7 )
666 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= MONTH
;
667 nDaysPerInterval
= 31.0;
668 nNumer
= static_cast<sal_Int32
>( rtl::math::approxFloor( nIntervalDays
/nDaysPerInterval
) );
673 rExplicitIncrement
.MajorTimeInterval
.Number
= nNumer
;
674 nMainIncrementCount
= static_cast<tools::Long
>(nDayCount
/(nNumer
*nDaysPerInterval
));
677 //choose minor time interval:
680 if( rExplicitIncrement
.MinorTimeInterval
.TimeUnit
> rExplicitIncrement
.MajorTimeInterval
.TimeUnit
)
681 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= rExplicitIncrement
.MajorTimeInterval
.TimeUnit
;
682 tools::Long nIntervalDayCount
= rExplicitIncrement
.MinorTimeInterval
.Number
;
683 switch( rExplicitIncrement
.MinorTimeInterval
.TimeUnit
)
688 nIntervalDayCount
*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
691 nIntervalDayCount
*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
694 if( nDayCount
/nIntervalDayCount
> nMaxMainIncrementCount
)
700 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= rExplicitIncrement
.MajorTimeInterval
.TimeUnit
;
701 rExplicitIncrement
.MinorTimeInterval
.Number
= 1;
702 if( nMainIncrementCount
> 100 )
703 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
;
706 if( rExplicitIncrement
.MajorTimeInterval
.Number
>= 2 )
708 if( !(rExplicitIncrement
.MajorTimeInterval
.Number
%2) )
709 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
/2;
710 else if( !(rExplicitIncrement
.MajorTimeInterval
.Number
%3) )
711 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
/3;
712 else if( !(rExplicitIncrement
.MajorTimeInterval
.Number
%5) )
713 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
/5;
714 else if( rExplicitIncrement
.MajorTimeInterval
.Number
> 50 )
715 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
;
719 switch( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
)
724 if( rExplicitScale
.TimeResolution
== DAY
)
725 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= DAY
;
728 if( rExplicitScale
.TimeResolution
<= MONTH
)
729 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= MONTH
;
737 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLinear(
738 ExplicitScaleData
& rExplicitScale
,
739 ExplicitIncrementData
& rExplicitIncrement
,
740 bool bAutoMinimum
, bool bAutoMaximum
) const
742 // *** STEP 1: initialize the range data ***
744 double fSourceMinimum
= rExplicitScale
.Minimum
;
745 double fSourceMaximum
= rExplicitScale
.Maximum
;
747 // set automatic PostEquidistant to true (maybe scaling dependent?)
748 if( !(m_aSourceScale
.IncrementData
.PostEquidistant
>>= rExplicitIncrement
.PostEquidistant
) )
749 rExplicitIncrement
.PostEquidistant
= true;
751 /* If range is invalid (minimum greater than maximum), change one of the
752 variable limits to validate the range. In this step, a zero-sized range
754 if( fSourceMinimum
> fSourceMaximum
)
756 // force changing the maximum, if both limits are fixed
757 if( bAutoMaximum
|| !bAutoMinimum
)
758 fSourceMaximum
= fSourceMinimum
;
760 fSourceMinimum
= fSourceMaximum
;
763 /* If maximum is zero or negative (and therefore minimum too), minimum and
764 maximum will be negated and swapped to make the following algorithms
765 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
766 [2,5], and the latter will be swapped back later. The range [0,0] is
767 explicitly excluded from swapping (this would result in [-1,0] instead
768 of the expected [0,1]). */
769 bool bSwapAndNegateRange
= (fSourceMinimum
< 0.0) && (fSourceMaximum
<= 0.0);
770 if( bSwapAndNegateRange
)
772 double fTempValue
= fSourceMinimum
;
773 fSourceMinimum
= -fSourceMaximum
;
774 fSourceMaximum
= -fTempValue
;
775 std::swap( bAutoMinimum
, bAutoMaximum
);
778 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
780 double fTempMinimum
= fSourceMinimum
;
781 double fTempMaximum
= fSourceMaximum
;
783 /* If minimum is variable and greater than 0 (and therefore maximum too),
784 means all values are positive (or all values are negative, and the
785 range has been swapped above), then: */
786 if( bAutoMinimum
&& (fTempMinimum
> 0.0) )
788 /* If minimum equals maximum, or if minimum is less than 5/6 of
789 maximum, set minimum to 0. */
790 if( (fTempMinimum
== fTempMaximum
) || (fTempMinimum
/ fTempMaximum
< 5.0 / 6.0) )
792 if( m_bExpandWideValuesToZero
)
795 /* Else (minimum is greater than or equal to 5/6 of maximum), add half
796 of the visible range (expand minimum toward 0) to make the
797 'shorter' data points visible. */
800 if( m_bExpandNarrowValuesTowardZero
)
801 fTempMinimum
-= (fTempMaximum
- fTempMinimum
) / 2.0;
805 /* If range is still zero-sized (e.g. when minimum is fixed), add some
806 space to a variable limit. */
807 if( fTempMinimum
== fTempMaximum
)
809 if( bAutoMaximum
|| !bAutoMinimum
)
811 // change 0 to 1, otherwise double the value
812 if( fTempMaximum
== 0.0 )
819 // change 0 to -1, otherwise halve the value
820 if( fTempMinimum
== 0.0 )
827 // *** STEP 3: calculate main interval size ***
829 // base value (anchor position of the intervals)
830 if( !(m_aSourceScale
.IncrementData
.BaseValue
>>= rExplicitIncrement
.BaseValue
) )
833 rExplicitIncrement
.BaseValue
= fTempMinimum
;
834 else if( !bAutoMaximum
)
835 rExplicitIncrement
.BaseValue
= fTempMaximum
;
837 rExplicitIncrement
.BaseValue
= 0.0;
840 // calculate automatic interval
841 bool bAutoDistance
= !(m_aSourceScale
.IncrementData
.Distance
>>= rExplicitIncrement
.Distance
);
842 /* Restrict number of allowed intervals with user-defined distance to
843 MAXIMUM_MANUAL_INCREMENT_COUNT. */
844 sal_Int32 nMaxMainIncrementCount
= bAutoDistance
?
845 m_nMaximumAutoMainIncrementCount
: MAXIMUM_MANUAL_INCREMENT_COUNT
;
847 double fDistanceMagnitude
= 0.0;
848 double fDistanceNormalized
= 0.0;
849 bool bHasNormalizedDistance
= false;
851 // repeat calculation until number of intervals are valid
852 bool bNeedIteration
= true;
853 while( bNeedIteration
)
857 // first iteration: calculate interval size from axis limits
858 if( !bHasNormalizedDistance
)
860 // raw size of an interval
861 double fDistance
= (fTempMaximum
- fTempMinimum
) / nMaxMainIncrementCount
;
863 // if distance of is less than 1e-307, do not do anything
864 if( fDistance
<= 1.0e-307 )
866 fDistanceNormalized
= 1.0;
867 fDistanceMagnitude
= 1.0e-307;
869 else if ( !std::isfinite(fDistance
) )
871 // fdo#43703: Handle values bigger than limits correctly
872 fDistanceNormalized
= 1.0;
873 fDistanceMagnitude
= std::numeric_limits
<double>::max();
877 // distance magnitude (a power of 10)
878 int nExponent
= static_cast< int >( ::rtl::math::approxFloor( log10( fDistance
) ) );
879 fDistanceMagnitude
= ::rtl::math::pow10Exp( 1.0, nExponent
);
881 // stick normalized distance to a few predefined values
882 fDistanceNormalized
= fDistance
/ fDistanceMagnitude
;
883 if( fDistanceNormalized
<= 1.0 )
884 fDistanceNormalized
= 1.0;
885 else if( fDistanceNormalized
<= 2.0 )
886 fDistanceNormalized
= 2.0;
887 else if( fDistanceNormalized
<= 5.0 )
888 fDistanceNormalized
= 5.0;
891 fDistanceNormalized
= 1.0;
892 fDistanceMagnitude
*= 10;
895 // for next iteration: distance is normalized -> use else path to increase distance
896 bHasNormalizedDistance
= true;
898 // following iterations: increase distance, use only allowed values
901 if( fDistanceNormalized
== 1.0 )
902 fDistanceNormalized
= 2.0;
903 else if( fDistanceNormalized
== 2.0 )
904 fDistanceNormalized
= 5.0;
907 fDistanceNormalized
= 1.0;
908 fDistanceMagnitude
*= 10;
912 // set the resulting distance
913 rExplicitIncrement
.Distance
= fDistanceNormalized
* fDistanceMagnitude
;
916 // *** STEP 4: additional space above or below the data points ***
918 double fAxisMinimum
= fTempMinimum
;
919 double fAxisMaximum
= fTempMaximum
;
921 // round to entire multiples of the distance and add additional space
924 // round to entire multiples of the distance, based on the base value
925 if( m_bExpandBorderToIncrementRhythm
)
926 fAxisMinimum
= EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum
, rExplicitIncrement
);
927 // additional space, if source minimum is to near at axis minimum
928 if( m_bExpandIfValuesCloseToBorder
)
929 if( (fAxisMinimum
!= 0.0) && ((fAxisMaximum
- fSourceMinimum
) / (fAxisMaximum
- fAxisMinimum
) > 20.0 / 21.0) )
930 fAxisMinimum
-= rExplicitIncrement
.Distance
;
934 // round to entire multiples of the distance, based on the base value
935 if( m_bExpandBorderToIncrementRhythm
)
936 fAxisMaximum
= EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum
, rExplicitIncrement
);
937 // additional space, if source maximum is to near at axis maximum
938 if( m_bExpandIfValuesCloseToBorder
)
939 if( (fAxisMaximum
!= 0.0) && ((fSourceMaximum
- fAxisMinimum
) / (fAxisMaximum
- fAxisMinimum
) > 20.0 / 21.0) )
940 fAxisMaximum
+= rExplicitIncrement
.Distance
;
943 // set the resulting limits (swap back to negative range if needed)
944 if( bSwapAndNegateRange
)
946 rExplicitScale
.Minimum
= -fAxisMaximum
;
947 rExplicitScale
.Maximum
= -fAxisMinimum
;
951 rExplicitScale
.Minimum
= fAxisMinimum
;
952 rExplicitScale
.Maximum
= fAxisMaximum
;
955 /* If the number of intervals is too high (e.g. due to invalid fixed
956 distance or due to added space above or below data points),
957 calculate again with increased distance. */
958 double fDistanceCount
= ::rtl::math::approxFloor( (fAxisMaximum
- fAxisMinimum
) / rExplicitIncrement
.Distance
);
959 bNeedIteration
= static_cast< sal_Int32
>( fDistanceCount
) > nMaxMainIncrementCount
;
960 // if manual distance is invalid, trigger automatic calculation
962 bAutoDistance
= true;
965 //fill explicit sub increment
966 sal_Int32 nSubCount
= m_aSourceScale
.IncrementData
.SubIncrements
.getLength();
967 for( sal_Int32 nN
=0; nN
<nSubCount
; nN
++ )
969 ExplicitSubIncrement aExplicitSubIncrement
;
970 const SubIncrement
& rSubIncrement
= m_aSourceScale
.IncrementData
.SubIncrements
[nN
];
971 if(!(rSubIncrement
.IntervalCount
>>=aExplicitSubIncrement
.IntervalCount
))
974 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
975 aExplicitSubIncrement
.IntervalCount
= 2;
977 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement
.IntervalCount
);
978 if(!(rSubIncrement
.PostEquidistant
>>=aExplicitSubIncrement
.PostEquidistant
))
981 aExplicitSubIncrement
.PostEquidistant
= false;
983 rExplicitIncrement
.SubIncrements
.push_back(aExplicitSubIncrement
);
989 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */