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"
22 #include "Tickmarks_Equidistant.hxx"
23 #include "DateHelper.hxx"
24 #include "DateScaling.hxx"
25 #include "AxisHelper.hxx"
26 #include <com/sun/star/chart/TimeUnit.hpp>
28 #include <rtl/math.hxx>
33 using namespace ::com::sun::star
;
34 using namespace ::com::sun::star::chart2
;
35 using ::com::sun::star::chart::TimeUnit::DAY
;
36 using ::com::sun::star::chart::TimeUnit::MONTH
;
37 using ::com::sun::star::chart::TimeUnit::YEAR
;
39 const sal_Int32 MAXIMUM_MANUAL_INCREMENT_COUNT
= 500;
40 const sal_Int32 MAXIMUM_SUB_INCREMENT_COUNT
= 100;
42 sal_Int32
lcl_getMaximumAutoIncrementCount( sal_Int32 nAxisType
)
44 sal_Int32 nMaximumAutoIncrementCount
= 10;
45 if( nAxisType
==AxisType::DATE
)
46 nMaximumAutoIncrementCount
= MAXIMUM_MANUAL_INCREMENT_COUNT
;
47 return nMaximumAutoIncrementCount
;
53 void lcl_ensureMaximumSubIncrementCount( sal_Int32
& rnSubIntervalCount
)
55 if( rnSubIntervalCount
> MAXIMUM_SUB_INCREMENT_COUNT
)
56 rnSubIntervalCount
= MAXIMUM_SUB_INCREMENT_COUNT
;
59 }//end anonymous namespace
61 ExplicitScaleData::ExplicitScaleData()
65 , Orientation(::com::sun::star::chart2::AxisOrientation_MATHEMATICAL
)
67 , AxisType(::com::sun::star::chart2::AxisType::REALNUMBER
)
68 , ShiftedCategoryPosition(false)
69 , TimeResolution(::com::sun::star::chart::TimeUnit::DAY
)
70 , NullDate(30,12,1899)
74 ExplicitSubIncrement::ExplicitSubIncrement()
76 , PostEquidistant(true)
80 ExplicitIncrementData::ExplicitIncrementData()
81 : MajorTimeInterval(1,::com::sun::star::chart::TimeUnit::DAY
)
82 , MinorTimeInterval(1,::com::sun::star::chart::TimeUnit::DAY
)
84 , PostEquidistant(true)
90 ScaleAutomatism::ScaleAutomatism( const ScaleData
& rSourceScale
, const Date
& rNullDate
)
91 : m_aSourceScale( rSourceScale
)
92 , m_fValueMinimum( 0.0 )
93 , m_fValueMaximum( 0.0 )
94 , m_nMaximumAutoMainIncrementCount( lcl_getMaximumAutoIncrementCount( rSourceScale
.AxisType
) )
95 , m_bExpandBorderToIncrementRhythm( false )
96 , m_bExpandIfValuesCloseToBorder( false )
97 , m_bExpandWideValuesToZero( false )
98 , m_bExpandNarrowValuesTowardZero( false )
99 , m_nTimeResolution(::com::sun::star::chart::TimeUnit::DAY
)
100 , m_aNullDate(rNullDate
)
102 ::rtl::math::setNan( &m_fValueMinimum
);
103 ::rtl::math::setNan( &m_fValueMaximum
);
105 double fExplicitOrigin
= 0.0;
106 if( m_aSourceScale
.Origin
>>= fExplicitOrigin
)
107 expandValueRange( fExplicitOrigin
, fExplicitOrigin
);
109 ScaleAutomatism::~ScaleAutomatism()
113 void ScaleAutomatism::expandValueRange( double fMinimum
, double fMaximum
)
115 if( (fMinimum
< m_fValueMinimum
) || ::rtl::math::isNan( m_fValueMinimum
) )
116 m_fValueMinimum
= fMinimum
;
117 if( (fMaximum
> m_fValueMaximum
) || ::rtl::math::isNan( m_fValueMaximum
) )
118 m_fValueMaximum
= fMaximum
;
121 void ScaleAutomatism::setAutoScalingOptions(
122 bool bExpandBorderToIncrementRhythm
,
123 bool bExpandIfValuesCloseToBorder
,
124 bool bExpandWideValuesToZero
,
125 bool bExpandNarrowValuesTowardZero
)
127 // if called multiple times, enable an option, if it is set in at least one call
128 m_bExpandBorderToIncrementRhythm
|= bExpandBorderToIncrementRhythm
;
129 m_bExpandIfValuesCloseToBorder
|= bExpandIfValuesCloseToBorder
;
130 m_bExpandWideValuesToZero
|= bExpandWideValuesToZero
;
131 m_bExpandNarrowValuesTowardZero
|= bExpandNarrowValuesTowardZero
;
133 if( m_aSourceScale
.AxisType
==AxisType::PERCENT
)
134 m_bExpandIfValuesCloseToBorder
= false;
137 void ScaleAutomatism::setMaximumAutoMainIncrementCount( sal_Int32 nMaximumAutoMainIncrementCount
)
139 if( nMaximumAutoMainIncrementCount
< 2 )
140 m_nMaximumAutoMainIncrementCount
= 2; //#i82006
141 else if( nMaximumAutoMainIncrementCount
> lcl_getMaximumAutoIncrementCount( m_aSourceScale
.AxisType
) )
142 m_nMaximumAutoMainIncrementCount
= lcl_getMaximumAutoIncrementCount( m_aSourceScale
.AxisType
);
144 m_nMaximumAutoMainIncrementCount
= nMaximumAutoMainIncrementCount
;
147 void ScaleAutomatism::setAutomaticTimeResolution( sal_Int32 nTimeResolution
)
149 m_nTimeResolution
= nTimeResolution
;
152 void ScaleAutomatism::calculateExplicitScaleAndIncrement(
153 ExplicitScaleData
& rExplicitScale
, ExplicitIncrementData
& rExplicitIncrement
) const
155 // fill explicit scale
156 rExplicitScale
.Orientation
= m_aSourceScale
.Orientation
;
157 rExplicitScale
.Scaling
= m_aSourceScale
.Scaling
;
158 rExplicitScale
.AxisType
= m_aSourceScale
.AxisType
;
159 rExplicitScale
.NullDate
= m_aNullDate
;
161 bool bAutoMinimum
= !(m_aSourceScale
.Minimum
>>= rExplicitScale
.Minimum
);
162 bool bAutoMaximum
= !(m_aSourceScale
.Maximum
>>= rExplicitScale
.Maximum
);
163 bool bAutoOrigin
= !(m_aSourceScale
.Origin
>>= rExplicitScale
.Origin
);
165 // automatic scale minimum
168 if( m_aSourceScale
.AxisType
==AxisType::PERCENT
)
169 rExplicitScale
.Minimum
= 0.0;
170 else if( ::rtl::math::isNan( m_fValueMinimum
) )
172 if( m_aSourceScale
.AxisType
==AxisType::DATE
)
173 rExplicitScale
.Minimum
= 36526.0; //1.1.2000
175 rExplicitScale
.Minimum
= 0.0; //@todo get Minimum from scaling or from plotter????
178 rExplicitScale
.Minimum
= m_fValueMinimum
;
181 // automatic scale maximum
184 if( m_aSourceScale
.AxisType
==AxisType::PERCENT
)
185 rExplicitScale
.Maximum
= 1.0;
186 else if( ::rtl::math::isNan( m_fValueMaximum
) )
188 if( m_aSourceScale
.AxisType
==AxisType::DATE
)
189 rExplicitScale
.Maximum
= 40179.0; //1.1.2010
191 rExplicitScale
.Maximum
= 10.0; //@todo get Maximum from scaling or from plotter????
194 rExplicitScale
.Maximum
= m_fValueMaximum
;
197 //fill explicit increment
199 rExplicitScale
.ShiftedCategoryPosition
= m_aSourceScale
.ShiftedCategoryPosition
;
200 bool bIsLogarithm
= false;
202 //minimum and maximum of the ExplicitScaleData may be changed if allowed
203 if( m_aSourceScale
.AxisType
==AxisType::DATE
)
204 calculateExplicitIncrementAndScaleForDateTimeAxis( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
205 else if( m_aSourceScale
.AxisType
==AxisType::CATEGORY
|| m_aSourceScale
.AxisType
==AxisType::SERIES
)
206 calculateExplicitIncrementAndScaleForCategory( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
209 bIsLogarithm
= AxisHelper::isLogarithmic( rExplicitScale
.Scaling
);
211 calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
213 calculateExplicitIncrementAndScaleForLinear( rExplicitScale
, rExplicitIncrement
, bAutoMinimum
, bAutoMaximum
);
219 // #i71415# automatic origin for logarithmic axis
220 double fDefaulOrigin
= bIsLogarithm
? 1.0 : 0.0;
222 if( fDefaulOrigin
< rExplicitScale
.Minimum
)
223 fDefaulOrigin
= rExplicitScale
.Minimum
;
224 else if( fDefaulOrigin
> rExplicitScale
.Maximum
)
225 fDefaulOrigin
= rExplicitScale
.Maximum
;
227 rExplicitScale
.Origin
= fDefaulOrigin
;
231 void ScaleAutomatism::calculateExplicitIncrementAndScaleForCategory(
232 ExplicitScaleData
& rExplicitScale
,
233 ExplicitIncrementData
& rExplicitIncrement
,
234 bool bAutoMinimum
, bool bAutoMaximum
) const
236 // no scaling for categories
237 rExplicitScale
.Scaling
.clear();
239 if( rExplicitScale
.ShiftedCategoryPosition
)
240 rExplicitScale
.Maximum
+= 1.0;
242 // ensure that at least one category is visible
243 if( rExplicitScale
.Maximum
<= rExplicitScale
.Minimum
)
244 rExplicitScale
.Maximum
= rExplicitScale
.Minimum
+ 1.0;
246 // default increment settings
247 rExplicitIncrement
.PostEquidistant
= true; // does not matter anyhow
248 rExplicitIncrement
.Distance
= 1.0; // category axis always have a main increment of 1
249 rExplicitIncrement
.BaseValue
= 0.0; // category axis always have a base of 0
251 // automatic minimum and maximum
252 if( bAutoMinimum
&& m_bExpandBorderToIncrementRhythm
)
253 rExplicitScale
.Minimum
= EquidistantTickFactory::getMinimumAtIncrement( rExplicitScale
.Minimum
, rExplicitIncrement
);
254 if( bAutoMaximum
&& m_bExpandBorderToIncrementRhythm
)
255 rExplicitScale
.Maximum
= EquidistantTickFactory::getMaximumAtIncrement( rExplicitScale
.Maximum
, rExplicitIncrement
);
257 //prevent performace killover
258 double fDistanceCount
= ::rtl::math::approxFloor( (rExplicitScale
.Maximum
-rExplicitScale
.Minimum
) / rExplicitIncrement
.Distance
);
259 if( static_cast< sal_Int32
>( fDistanceCount
) > MAXIMUM_MANUAL_INCREMENT_COUNT
)
261 double fMinimumFloor
= ::rtl::math::approxFloor( rExplicitScale
.Minimum
);
262 double fMaximumCeil
= ::rtl::math::approxCeil( rExplicitScale
.Maximum
);
263 rExplicitIncrement
.Distance
= ::rtl::math::approxCeil( (fMaximumCeil
- fMinimumFloor
) / MAXIMUM_MANUAL_INCREMENT_COUNT
);
266 //fill explicit sub increment
267 sal_Int32 nSubCount
= m_aSourceScale
.IncrementData
.SubIncrements
.getLength();
268 for( sal_Int32 nN
=0; nN
<nSubCount
; nN
++ )
270 ExplicitSubIncrement aExplicitSubIncrement
;
271 const SubIncrement
& rSubIncrement
= m_aSourceScale
.IncrementData
.SubIncrements
[nN
];
272 if(!(rSubIncrement
.IntervalCount
>>=aExplicitSubIncrement
.IntervalCount
))
275 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
276 aExplicitSubIncrement
.IntervalCount
= 2;
278 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement
.IntervalCount
);
279 if(!(rSubIncrement
.PostEquidistant
>>=aExplicitSubIncrement
.PostEquidistant
))
282 aExplicitSubIncrement
.PostEquidistant
= false;
284 rExplicitIncrement
.SubIncrements
.push_back(aExplicitSubIncrement
);
288 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLogarithmic(
289 ExplicitScaleData
& rExplicitScale
,
290 ExplicitIncrementData
& rExplicitIncrement
,
291 bool bAutoMinimum
, bool bAutoMaximum
) const
293 // *** STEP 1: initialize the range data ***
295 const double fInputMinimum
= rExplicitScale
.Minimum
;
296 const double fInputMaximum
= rExplicitScale
.Maximum
;
298 double fSourceMinimum
= rExplicitScale
.Minimum
;
299 double fSourceMaximum
= rExplicitScale
.Maximum
;
301 // set automatic PostEquidistant to true (maybe scaling dependent?)
302 // Note: scaling with PostEquidistant==false is untested and needs review
303 if( !(m_aSourceScale
.IncrementData
.PostEquidistant
>>= rExplicitIncrement
.PostEquidistant
) )
304 rExplicitIncrement
.PostEquidistant
= true;
306 /* All following scaling code will operate on the logarithms of the source
307 values. In the last step, the original values will be restored. */
308 uno::Reference
< XScaling
> xScaling
= rExplicitScale
.Scaling
;
310 xScaling
.set( AxisHelper::createLogarithmicScaling() );
311 uno::Reference
< XScaling
> xInverseScaling
= xScaling
->getInverseScaling();
313 fSourceMinimum
= xScaling
->doScaling( fSourceMinimum
);
314 if( !::rtl::math::isFinite( fSourceMinimum
) )
315 fSourceMinimum
= 0.0;
316 else if( ::rtl::math::approxEqual( fSourceMinimum
, ::rtl::math::approxFloor( fSourceMinimum
) ) )
317 fSourceMinimum
= ::rtl::math::approxFloor( fSourceMinimum
);
319 fSourceMaximum
= xScaling
->doScaling( fSourceMaximum
);
320 if( !::rtl::math::isFinite( fSourceMaximum
) )
321 fSourceMaximum
= 0.0;
322 else if( ::rtl::math::approxEqual( fSourceMaximum
, ::rtl::math::approxFloor( fSourceMaximum
) ) )
323 fSourceMaximum
= ::rtl::math::approxFloor( fSourceMaximum
);
325 /* If range is invalid (minimum greater than maximum), change one of the
326 variable limits to validate the range. In this step, a zero-sized range
328 if( fSourceMinimum
> fSourceMaximum
)
330 // force changing the maximum, if both limits are fixed
331 if( bAutoMaximum
|| !bAutoMinimum
)
332 fSourceMaximum
= fSourceMinimum
;
334 fSourceMinimum
= fSourceMaximum
;
337 /* If maximum is less than 0 (and therefore minimum too), minimum and
338 maximum will be negated and swapped to make the following algorithms
339 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
340 [2,5], and the latter will be swapped back later. The range [0,0] is
341 explicitly excluded from swapping (this would result in [-1,0] instead
342 of the expected [0,1]). */
343 bool bSwapAndNegateRange
= (fSourceMinimum
< 0.0) && (fSourceMaximum
<= 0.0);
344 if( bSwapAndNegateRange
)
346 double fTempValue
= fSourceMinimum
;
347 fSourceMinimum
= -fSourceMaximum
;
348 fSourceMaximum
= -fTempValue
;
349 ::std::swap( bAutoMinimum
, bAutoMaximum
);
352 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
354 double fTempMinimum
= fSourceMinimum
;
355 double fTempMaximum
= fSourceMaximum
;
357 /* If minimum is variable and greater than 0 (and therefore maximum too),
358 means all original values are greater than 1 (or all values are less
359 than 1, and the range has been swapped above), then: */
360 if( bAutoMinimum
&& (fTempMinimum
> 0.0) )
362 /* If minimum is less than 5 (i.e. original source values less than
363 B^5, B being the base of the scaling), or if minimum and maximum
364 are in different increment intervals (means, if minimum and maximum
365 are not both in the range [B^n,B^(n+1)] for a whole number n), set
366 minimum to 0, which results in B^0=1 on the axis. */
367 double fMinimumFloor
= ::rtl::math::approxFloor( fTempMinimum
);
368 double fMaximumFloor
= ::rtl::math::approxFloor( fTempMaximum
);
369 // handle the exact value B^(n+1) to be in the range [B^n,B^(n+1)]
370 if( ::rtl::math::approxEqual( fTempMaximum
, fMaximumFloor
) )
371 fMaximumFloor
-= 1.0;
373 if( (fMinimumFloor
< 5.0) || (fMinimumFloor
< fMaximumFloor
) )
375 if( m_bExpandWideValuesToZero
)
378 /* Else (minimum and maximum are in one increment interval), expand
379 minimum toward 0 to make the 'shorter' data points visible. */
382 if( m_bExpandNarrowValuesTowardZero
)
387 /* If range is still zero-sized (e.g. when minimum is fixed), set minimum
388 to 0, which makes the axis start/stop at the value 1. */
389 if( fTempMinimum
== fTempMaximum
)
391 if( bAutoMinimum
&& (fTempMaximum
> 0.0) )
394 fTempMaximum
+= 1.0; // always add one interval, even if maximum is fixed
397 // *** STEP 3: calculate main interval size ***
399 // base value (anchor position of the intervals), already scaled
400 if( !(m_aSourceScale
.IncrementData
.BaseValue
>>= rExplicitIncrement
.BaseValue
) )
403 //@maybe todo is this default also plotter dependent ??
405 rExplicitIncrement
.BaseValue
= fTempMinimum
;
406 else if( !bAutoMaximum
)
407 rExplicitIncrement
.BaseValue
= fTempMaximum
;
409 rExplicitIncrement
.BaseValue
= 0.0;
412 // calculate automatic interval
413 bool bAutoDistance
= !(m_aSourceScale
.IncrementData
.Distance
>>= rExplicitIncrement
.Distance
);
415 rExplicitIncrement
.Distance
= 0.0;
417 /* Restrict number of allowed intervals with user-defined distance to
418 MAXIMUM_MANUAL_INCREMENT_COUNT. */
419 sal_Int32 nMaxMainIncrementCount
= bAutoDistance
?
420 m_nMaximumAutoMainIncrementCount
: MAXIMUM_MANUAL_INCREMENT_COUNT
;
422 // repeat calculation until number of intervals are valid
423 bool bNeedIteration
= true;
424 bool bHasCalculatedDistance
= false;
425 while( bNeedIteration
)
429 // first iteration: calculate interval size from axis limits
430 if( !bHasCalculatedDistance
)
432 double fMinimumFloor
= ::rtl::math::approxFloor( fTempMinimum
);
433 double fMaximumCeil
= ::rtl::math::approxCeil( fTempMaximum
);
434 rExplicitIncrement
.Distance
= ::rtl::math::approxCeil( (fMaximumCeil
- fMinimumFloor
) / nMaxMainIncrementCount
);
438 // following iterations: increase distance
439 rExplicitIncrement
.Distance
+= 1.0;
442 // for next iteration: distance calculated -> use else path to increase
443 bHasCalculatedDistance
= true;
446 // *** STEP 4: additional space above or below the data points ***
448 double fAxisMinimum
= fTempMinimum
;
449 double fAxisMaximum
= fTempMaximum
;
451 // round to entire multiples of the distance and add additional space
452 if( bAutoMinimum
&& m_bExpandBorderToIncrementRhythm
)
454 fAxisMinimum
= EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum
, rExplicitIncrement
);
456 //ensure valid values after scaling #i100995#
459 double fCheck
= xInverseScaling
->doScaling( fAxisMinimum
);
460 if( !::rtl::math::isFinite( fCheck
) || fCheck
<= 0 )
462 bAutoDistance
= true;
463 bHasCalculatedDistance
= false;
468 if( bAutoMaximum
&& m_bExpandBorderToIncrementRhythm
)
470 fAxisMaximum
= EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum
, rExplicitIncrement
);
472 //ensure valid values after scaling #i100995#
475 double fCheck
= xInverseScaling
->doScaling( fAxisMaximum
);
476 if( !::rtl::math::isFinite( fCheck
) || fCheck
<= 0 )
478 bAutoDistance
= true;
479 bHasCalculatedDistance
= false;
485 // set the resulting limits (swap back to negative range if needed)
486 if( bSwapAndNegateRange
)
488 rExplicitScale
.Minimum
= -fAxisMaximum
;
489 rExplicitScale
.Maximum
= -fAxisMinimum
;
493 rExplicitScale
.Minimum
= fAxisMinimum
;
494 rExplicitScale
.Maximum
= fAxisMaximum
;
497 /* If the number of intervals is too high (e.g. due to invalid fixed
498 distance or due to added space above or below data points),
499 calculate again with increased distance. */
500 double fDistanceCount
= ::rtl::math::approxFloor( (fAxisMaximum
- fAxisMinimum
) / rExplicitIncrement
.Distance
);
501 bNeedIteration
= static_cast< sal_Int32
>( fDistanceCount
) > nMaxMainIncrementCount
;
502 // if manual distance is invalid, trigger automatic calculation
504 bAutoDistance
= true;
506 // convert limits back to logarithmic scale
507 rExplicitScale
.Minimum
= xInverseScaling
->doScaling( rExplicitScale
.Minimum
);
508 rExplicitScale
.Maximum
= xInverseScaling
->doScaling( rExplicitScale
.Maximum
);
510 //ensure valid values after scaling #i100995#
511 if( !::rtl::math::isFinite( rExplicitScale
.Minimum
) || rExplicitScale
.Minimum
<= 0)
513 rExplicitScale
.Minimum
= fInputMinimum
;
514 if( !::rtl::math::isFinite( rExplicitScale
.Minimum
) || rExplicitScale
.Minimum
<= 0 )
515 rExplicitScale
.Minimum
= 1.0;
517 if( !::rtl::math::isFinite( rExplicitScale
.Maximum
) || rExplicitScale
.Maximum
<= 0 )
519 rExplicitScale
.Maximum
= fInputMaximum
;
520 if( !::rtl::math::isFinite( rExplicitScale
.Maximum
) || rExplicitScale
.Maximum
<= 0 )
521 rExplicitScale
.Maximum
= 10.0;
523 if( rExplicitScale
.Maximum
< rExplicitScale
.Minimum
)
524 ::std::swap( rExplicitScale
.Maximum
, rExplicitScale
.Minimum
);
527 //fill explicit sub increment
528 sal_Int32 nSubCount
= m_aSourceScale
.IncrementData
.SubIncrements
.getLength();
529 for( sal_Int32 nN
=0; nN
<nSubCount
; nN
++ )
531 ExplicitSubIncrement aExplicitSubIncrement
;
532 const SubIncrement
& rSubIncrement
= m_aSourceScale
.IncrementData
.SubIncrements
[nN
];
533 if(!(rSubIncrement
.IntervalCount
>>=aExplicitSubIncrement
.IntervalCount
))
536 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
537 aExplicitSubIncrement
.IntervalCount
= 9;
539 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement
.IntervalCount
);
540 if(!(rSubIncrement
.PostEquidistant
>>=aExplicitSubIncrement
.PostEquidistant
))
543 aExplicitSubIncrement
.PostEquidistant
= false;
545 rExplicitIncrement
.SubIncrements
.push_back(aExplicitSubIncrement
);
549 void ScaleAutomatism::calculateExplicitIncrementAndScaleForDateTimeAxis(
550 ExplicitScaleData
& rExplicitScale
,
551 ExplicitIncrementData
& rExplicitIncrement
,
552 bool bAutoMinimum
, bool bAutoMaximum
) const
554 Date
aMinDate(m_aNullDate
); aMinDate
+= static_cast<long>(::rtl::math::approxFloor(rExplicitScale
.Minimum
));
555 Date
aMaxDate(m_aNullDate
); aMaxDate
+= static_cast<long>(::rtl::math::approxFloor(rExplicitScale
.Maximum
));
556 rExplicitIncrement
.PostEquidistant
= false;
558 if( aMinDate
> aMaxDate
)
560 std::swap(aMinDate
,aMaxDate
);
563 if( !(m_aSourceScale
.TimeIncrement
.TimeResolution
>>= rExplicitScale
.TimeResolution
) )
564 rExplicitScale
.TimeResolution
= m_nTimeResolution
;
566 rExplicitScale
.Scaling
= new DateScaling(m_aNullDate
,rExplicitScale
.TimeResolution
,false);
568 // choose min and max suitable to time resolution
569 switch( rExplicitScale
.TimeResolution
)
572 if( rExplicitScale
.ShiftedCategoryPosition
)
573 ++aMaxDate
; //for explicit scales we need one interval more (maximum excluded)
578 if( rExplicitScale
.ShiftedCategoryPosition
)
579 aMaxDate
= DateHelper::GetDateSomeMonthsAway(aMaxDate
,1);//for explicit scales we need one interval more (maximum excluded)
580 if( DateHelper::IsLessThanOneMonthAway( aMinDate
, aMaxDate
) )
582 if( bAutoMaximum
|| !bAutoMinimum
)
583 aMaxDate
= DateHelper::GetDateSomeMonthsAway(aMinDate
,1);
585 aMinDate
= DateHelper::GetDateSomeMonthsAway(aMaxDate
,-1);
590 aMinDate
.SetMonth(1);
592 aMaxDate
.SetMonth(1);
593 if( rExplicitScale
.ShiftedCategoryPosition
)
594 aMaxDate
= DateHelper::GetDateSomeYearsAway(aMaxDate
,1);//for explicit scales we need one interval more (maximum excluded)
595 if( DateHelper::IsLessThanOneYearAway( aMinDate
, aMaxDate
) )
597 if( bAutoMaximum
|| !bAutoMinimum
)
598 aMaxDate
= DateHelper::GetDateSomeYearsAway(aMinDate
,1);
600 aMinDate
= DateHelper::GetDateSomeYearsAway(aMaxDate
,-1);
605 // set the resulting limits (swap back to negative range if needed)
606 rExplicitScale
.Minimum
= aMinDate
- m_aNullDate
;
607 rExplicitScale
.Maximum
= aMaxDate
- m_aNullDate
;
609 bool bAutoMajor
= !(m_aSourceScale
.TimeIncrement
.MajorTimeInterval
>>= rExplicitIncrement
.MajorTimeInterval
);
610 bool bAutoMinor
= !(m_aSourceScale
.TimeIncrement
.MinorTimeInterval
>>= rExplicitIncrement
.MinorTimeInterval
);
612 sal_Int32 nMaxMainIncrementCount
= bAutoMajor
?
613 m_nMaximumAutoMainIncrementCount
: MAXIMUM_MANUAL_INCREMENT_COUNT
;
614 if( nMaxMainIncrementCount
> 1 )
615 nMaxMainIncrementCount
--;
617 //choose major time interval:
618 long nDayCount
= (aMaxDate
-aMinDate
);
619 long nMainIncrementCount
= 1;
622 long nIntervalDayCount
= rExplicitIncrement
.MajorTimeInterval
.Number
;
623 if( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
< rExplicitScale
.TimeResolution
)
624 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= rExplicitScale
.TimeResolution
;
625 switch( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
)
630 nIntervalDayCount
*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
633 nIntervalDayCount
*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
636 nMainIncrementCount
= nDayCount
/nIntervalDayCount
;
637 if( nMainIncrementCount
> nMaxMainIncrementCount
)
643 long nIntervalDays
= nDayCount
/ nMaxMainIncrementCount
;
644 double nDaysPerInterval
= 1.0;
645 if( nIntervalDays
>365 || YEAR
==rExplicitScale
.TimeResolution
)
647 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= YEAR
;
648 nDaysPerInterval
= 365.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
650 else if( nIntervalDays
>31 || MONTH
==rExplicitScale
.TimeResolution
)
652 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= MONTH
;
653 nDaysPerInterval
= 31.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
657 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= DAY
;
658 nDaysPerInterval
= 1.0;
661 nNumer
= static_cast<sal_Int32
>( rtl::math::approxCeil( nIntervalDays
/nDaysPerInterval
) );
664 if( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
== DAY
)
666 if( nNumer
>2 && nNumer
<7 )
670 rExplicitIncrement
.MajorTimeInterval
.TimeUnit
= MONTH
;
671 nDaysPerInterval
= 31.0;
672 nNumer
= static_cast<sal_Int32
>( rtl::math::approxCeil( nIntervalDays
/nDaysPerInterval
) );
677 rExplicitIncrement
.MajorTimeInterval
.Number
= nNumer
;
678 nMainIncrementCount
= static_cast<long>(nDayCount
/(nNumer
*nDaysPerInterval
));
681 //choose minor time interval:
684 if( rExplicitIncrement
.MinorTimeInterval
.TimeUnit
> rExplicitIncrement
.MajorTimeInterval
.TimeUnit
)
685 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= rExplicitIncrement
.MajorTimeInterval
.TimeUnit
;
686 long nIntervalDayCount
= rExplicitIncrement
.MinorTimeInterval
.Number
;
687 switch( rExplicitIncrement
.MinorTimeInterval
.TimeUnit
)
692 nIntervalDayCount
*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
695 nIntervalDayCount
*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
698 if( nDayCount
/nIntervalDayCount
> nMaxMainIncrementCount
)
703 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= rExplicitIncrement
.MajorTimeInterval
.TimeUnit
;
704 rExplicitIncrement
.MinorTimeInterval
.Number
= 1;
705 if( nMainIncrementCount
> 100 )
706 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
;
709 if( rExplicitIncrement
.MajorTimeInterval
.Number
>= 2 )
711 if( !(rExplicitIncrement
.MajorTimeInterval
.Number
%2) )
712 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
/2;
713 else if( !(rExplicitIncrement
.MajorTimeInterval
.Number
%3) )
714 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
/3;
715 else if( !(rExplicitIncrement
.MajorTimeInterval
.Number
%5) )
716 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
/5;
717 else if( rExplicitIncrement
.MajorTimeInterval
.Number
> 50 )
718 rExplicitIncrement
.MinorTimeInterval
.Number
= rExplicitIncrement
.MajorTimeInterval
.Number
;
722 switch( rExplicitIncrement
.MajorTimeInterval
.TimeUnit
)
727 if( rExplicitScale
.TimeResolution
== DAY
)
728 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= DAY
;
731 if( rExplicitScale
.TimeResolution
<= MONTH
)
732 rExplicitIncrement
.MinorTimeInterval
.TimeUnit
= MONTH
;
741 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLinear(
742 ExplicitScaleData
& rExplicitScale
,
743 ExplicitIncrementData
& rExplicitIncrement
,
744 bool bAutoMinimum
, bool bAutoMaximum
) const
746 // *** STEP 1: initialize the range data ***
748 double fSourceMinimum
= rExplicitScale
.Minimum
;
749 double fSourceMaximum
= rExplicitScale
.Maximum
;
751 // set automatic PostEquidistant to true (maybe scaling dependent?)
752 if( !(m_aSourceScale
.IncrementData
.PostEquidistant
>>= rExplicitIncrement
.PostEquidistant
) )
753 rExplicitIncrement
.PostEquidistant
= true;
755 /* If range is invalid (minimum greater than maximum), change one of the
756 variable limits to validate the range. In this step, a zero-sized range
758 if( fSourceMinimum
> fSourceMaximum
)
760 // force changing the maximum, if both limits are fixed
761 if( bAutoMaximum
|| !bAutoMinimum
)
762 fSourceMaximum
= fSourceMinimum
;
764 fSourceMinimum
= fSourceMaximum
;
767 /* If maximum is zero or negative (and therefore minimum too), minimum and
768 maximum will be negated and swapped to make the following algorithms
769 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
770 [2,5], and the latter will be swapped back later. The range [0,0] is
771 explicitly excluded from swapping (this would result in [-1,0] instead
772 of the expected [0,1]). */
773 bool bSwapAndNegateRange
= (fSourceMinimum
< 0.0) && (fSourceMaximum
<= 0.0);
774 if( bSwapAndNegateRange
)
776 double fTempValue
= fSourceMinimum
;
777 fSourceMinimum
= -fSourceMaximum
;
778 fSourceMaximum
= -fTempValue
;
779 ::std::swap( bAutoMinimum
, bAutoMaximum
);
782 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
784 double fTempMinimum
= fSourceMinimum
;
785 double fTempMaximum
= fSourceMaximum
;
787 /* If minimum is variable and greater than 0 (and therefore maximum too),
788 means all values are positive (or all values are negative, and the
789 range has been swapped above), then: */
790 if( bAutoMinimum
&& (fTempMinimum
> 0.0) )
792 /* If minimum equals maximum, or if minimum is less than 5/6 of
793 maximum, set minimum to 0. */
794 if( (fTempMinimum
== fTempMaximum
) || (fTempMinimum
/ fTempMaximum
< 5.0 / 6.0) )
796 if( m_bExpandWideValuesToZero
)
799 /* Else (minimum is greater than or equal to 5/6 of maximum), add half
800 of the visible range (expand minimum toward 0) to make the
801 'shorter' data points visible. */
804 if( m_bExpandNarrowValuesTowardZero
)
805 fTempMinimum
-= (fTempMaximum
- fTempMinimum
) / 2.0;
809 /* If range is still zero-sized (e.g. when minimum is fixed), add some
810 space to a variable limit. */
811 if( fTempMinimum
== fTempMaximum
)
813 if( bAutoMaximum
|| !bAutoMinimum
)
815 // change 0 to 1, otherwise double the value
816 if( fTempMaximum
== 0.0 )
823 // change 0 to -1, otherwise halve the value
824 if( fTempMinimum
== 0.0 )
831 // *** STEP 3: calculate main interval size ***
833 // base value (anchor position of the intervals)
834 if( !(m_aSourceScale
.IncrementData
.BaseValue
>>= rExplicitIncrement
.BaseValue
) )
837 rExplicitIncrement
.BaseValue
= fTempMinimum
;
838 else if( !bAutoMaximum
)
839 rExplicitIncrement
.BaseValue
= fTempMaximum
;
841 rExplicitIncrement
.BaseValue
= 0.0;
844 // calculate automatic interval
845 bool bAutoDistance
= !(m_aSourceScale
.IncrementData
.Distance
>>= rExplicitIncrement
.Distance
);
846 /* Restrict number of allowed intervals with user-defined distance to
847 MAXIMUM_MANUAL_INCREMENT_COUNT. */
848 sal_Int32 nMaxMainIncrementCount
= bAutoDistance
?
849 m_nMaximumAutoMainIncrementCount
: MAXIMUM_MANUAL_INCREMENT_COUNT
;
851 double fDistanceMagnitude
= 0.0;
852 double fDistanceNormalized
= 0.0;
853 bool bHasNormalizedDistance
= false;
855 // repeat calculation until number of intervals are valid
856 bool bNeedIteration
= true;
857 while( bNeedIteration
)
861 // first iteration: calculate interval size from axis limits
862 if( !bHasNormalizedDistance
)
864 // raw size of an interval
865 double fDistance
= (fTempMaximum
- fTempMinimum
) / nMaxMainIncrementCount
;
867 // if distance of is less than 1e-307, do not do anything
868 if( fDistance
<= 1.0e-307 )
870 fDistanceNormalized
= 1.0;
871 fDistanceMagnitude
= 1.0e-307;
873 else if ( !rtl::math::isFinite(fDistance
) )
875 // fdo#43703: Handle values bigger than limits correctly
876 fDistanceNormalized
= 1.0;
877 fDistanceMagnitude
= std::numeric_limits
<double>::max();
881 // distance magnitude (a power of 10)
882 int nExponent
= static_cast< int >( ::rtl::math::approxFloor( log10( fDistance
) ) );
883 fDistanceMagnitude
= ::rtl::math::pow10Exp( 1.0, nExponent
);
885 // stick normalized distance to a few predefined values
886 fDistanceNormalized
= fDistance
/ fDistanceMagnitude
;
887 if( fDistanceNormalized
<= 1.0 )
888 fDistanceNormalized
= 1.0;
889 else if( fDistanceNormalized
<= 2.0 )
890 fDistanceNormalized
= 2.0;
891 else if( fDistanceNormalized
<= 5.0 )
892 fDistanceNormalized
= 5.0;
895 fDistanceNormalized
= 1.0;
896 fDistanceMagnitude
*= 10;
899 // for next iteration: distance is normalized -> use else path to increase distance
900 bHasNormalizedDistance
= true;
902 // following iterations: increase distance, use only allowed values
905 if( fDistanceNormalized
== 1.0 )
906 fDistanceNormalized
= 2.0;
907 else if( fDistanceNormalized
== 2.0 )
908 fDistanceNormalized
= 5.0;
911 fDistanceNormalized
= 1.0;
912 fDistanceMagnitude
*= 10;
916 // set the resulting distance
917 rExplicitIncrement
.Distance
= fDistanceNormalized
* fDistanceMagnitude
;
920 // *** STEP 4: additional space above or below the data points ***
922 double fAxisMinimum
= fTempMinimum
;
923 double fAxisMaximum
= fTempMaximum
;
925 // round to entire multiples of the distance and add additional space
928 // round to entire multiples of the distance, based on the base value
929 if( m_bExpandBorderToIncrementRhythm
)
930 fAxisMinimum
= EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum
, rExplicitIncrement
);
931 // additional space, if source minimum is to near at axis minimum
932 if( m_bExpandIfValuesCloseToBorder
)
933 if( (fAxisMinimum
!= 0.0) && ((fAxisMaximum
- fSourceMinimum
) / (fAxisMaximum
- fAxisMinimum
) > 20.0 / 21.0) )
934 fAxisMinimum
-= rExplicitIncrement
.Distance
;
938 // round to entire multiples of the distance, based on the base value
939 if( m_bExpandBorderToIncrementRhythm
)
940 fAxisMaximum
= EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum
, rExplicitIncrement
);
941 // additional space, if source maximum is to near at axis maximum
942 if( m_bExpandIfValuesCloseToBorder
)
943 if( (fAxisMaximum
!= 0.0) && ((fSourceMaximum
- fAxisMinimum
) / (fAxisMaximum
- fAxisMinimum
) > 20.0 / 21.0) )
944 fAxisMaximum
+= rExplicitIncrement
.Distance
;
947 // set the resulting limits (swap back to negative range if needed)
948 if( bSwapAndNegateRange
)
950 rExplicitScale
.Minimum
= -fAxisMaximum
;
951 rExplicitScale
.Maximum
= -fAxisMinimum
;
955 rExplicitScale
.Minimum
= fAxisMinimum
;
956 rExplicitScale
.Maximum
= fAxisMaximum
;
959 /* If the number of intervals is too high (e.g. due to invalid fixed
960 distance or due to added space above or below data points),
961 calculate again with increased distance. */
962 double fDistanceCount
= ::rtl::math::approxFloor( (fAxisMaximum
- fAxisMinimum
) / rExplicitIncrement
.Distance
);
963 bNeedIteration
= static_cast< sal_Int32
>( fDistanceCount
) > nMaxMainIncrementCount
;
964 // if manual distance is invalid, trigger automatic calculation
966 bAutoDistance
= true;
969 //fill explicit sub increment
970 sal_Int32 nSubCount
= m_aSourceScale
.IncrementData
.SubIncrements
.getLength();
971 for( sal_Int32 nN
=0; nN
<nSubCount
; nN
++ )
973 ExplicitSubIncrement aExplicitSubIncrement
;
974 const SubIncrement
& rSubIncrement
= m_aSourceScale
.IncrementData
.SubIncrements
[nN
];
975 if(!(rSubIncrement
.IntervalCount
>>=aExplicitSubIncrement
.IntervalCount
))
978 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
979 aExplicitSubIncrement
.IntervalCount
= 2;
981 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement
.IntervalCount
);
982 if(!(rSubIncrement
.PostEquidistant
>>=aExplicitSubIncrement
.PostEquidistant
))
985 aExplicitSubIncrement
.PostEquidistant
= false;
987 rExplicitIncrement
.SubIncrements
.push_back(aExplicitSubIncrement
);
993 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */