1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_chart2.hxx"
30 #include "ScaleAutomatism.hxx"
31 #include "macros.hxx"
32 #include "Tickmarks_Equidistant.hxx"
33 #include "DateHelper.hxx"
34 #include "DateScaling.hxx"
35 #include "AxisHelper.hxx"
36 #include <com/sun/star/chart/TimeUnit.hpp>
37 
38 #include <rtl/math.hxx>
39 #include <tools/debug.hxx>
40 
41 //.............................................................................
42 namespace chart
43 {
44 //.............................................................................
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::chart2;
47 using ::com::sun::star::chart::TimeUnit::DAY;
48 using ::com::sun::star::chart::TimeUnit::MONTH;
49 using ::com::sun::star::chart::TimeUnit::YEAR;
50 
51 const sal_Int32 MAXIMUM_MANUAL_INCREMENT_COUNT = 500;
52 const sal_Int32 MAXIMUM_SUB_INCREMENT_COUNT = 100;
53 
54 sal_Int32 lcl_getMaximumAutoIncrementCount( sal_Int32 nAxisType )
55 {
56     sal_Int32 nMaximumAutoIncrementCount = 10;
57     if( nAxisType==AxisType::DATE )
58         nMaximumAutoIncrementCount = MAXIMUM_MANUAL_INCREMENT_COUNT;
59     return nMaximumAutoIncrementCount;
60 }
61 
62 namespace
63 {
64 
65 void lcl_ensureMaximumSubIncrementCount( sal_Int32& rnSubIntervalCount )
66 {
67     if( rnSubIntervalCount > MAXIMUM_SUB_INCREMENT_COUNT )
68         rnSubIntervalCount = MAXIMUM_SUB_INCREMENT_COUNT;
69 }
70 
71 }//end anonymous namespace
72 
73 
74 //.............................................................................
75 
76 ExplicitScaleData::ExplicitScaleData()
77     : Minimum(0.0)
78     , Maximum(10.0)
79     , Origin(0.0)
80     , Orientation(::com::sun::star::chart2::AxisOrientation_MATHEMATICAL)
81     , Scaling()
82     , AxisType(::com::sun::star::chart2::AxisType::REALNUMBER)
83     , ShiftedCategoryPosition(false)
84     , TimeResolution(::com::sun::star::chart::TimeUnit::DAY)
85     , NullDate(30,12,1899)
86 {
87 }
88 
89 ExplicitSubIncrement::ExplicitSubIncrement()
90     : IntervalCount(2)
91     , PostEquidistant(true)
92 {
93 }
94 
95 
96 ExplicitIncrementData::ExplicitIncrementData()
97     : MajorTimeInterval(1,::com::sun::star::chart::TimeUnit::DAY)
98     , MinorTimeInterval(1,::com::sun::star::chart::TimeUnit::DAY)
99     , Distance(1.0)
100 	, PostEquidistant(true)
101 	, BaseValue(0.0)
102     , SubIncrements()
103 {
104 }
105 
106 //.............................................................................
107 
108 ScaleAutomatism::ScaleAutomatism( const ScaleData& rSourceScale, const Date& rNullDate )
109                     : m_aSourceScale( rSourceScale )
110                     , m_fValueMinimum( 0.0 )
111                     , m_fValueMaximum( 0.0 )
112                     , m_nMaximumAutoMainIncrementCount( lcl_getMaximumAutoIncrementCount( rSourceScale.AxisType ) )
113                     , m_bExpandBorderToIncrementRhythm( false )
114                     , m_bExpandIfValuesCloseToBorder( false )
115                     , m_bExpandWideValuesToZero( false )
116                     , m_bExpandNarrowValuesTowardZero( false )
117                     , m_nTimeResolution(::com::sun::star::chart::TimeUnit::DAY)
118                     , m_aNullDate(rNullDate)
119 {
120     ::rtl::math::setNan( &m_fValueMinimum );
121     ::rtl::math::setNan( &m_fValueMaximum );
122 
123     double fExplicitOrigin = 0.0;
124     if( m_aSourceScale.Origin >>= fExplicitOrigin )
125         expandValueRange( fExplicitOrigin, fExplicitOrigin);
126 }
127 ScaleAutomatism::~ScaleAutomatism()
128 {
129 }
130 
131 void ScaleAutomatism::expandValueRange( double fMinimum, double fMaximum )
132 {
133     if( (fMinimum < m_fValueMinimum) || ::rtl::math::isNan( m_fValueMinimum ) )
134         m_fValueMinimum = fMinimum;
135     if( (fMaximum > m_fValueMaximum) || ::rtl::math::isNan( m_fValueMaximum ) )
136         m_fValueMaximum = fMaximum;
137 }
138 
139 void ScaleAutomatism::setAutoScalingOptions(
140         bool bExpandBorderToIncrementRhythm,
141         bool bExpandIfValuesCloseToBorder,
142         bool bExpandWideValuesToZero,
143         bool bExpandNarrowValuesTowardZero )
144 {
145     // if called multiple times, enable an option, if it is set in at least one call
146     m_bExpandBorderToIncrementRhythm |= bExpandBorderToIncrementRhythm;
147     m_bExpandIfValuesCloseToBorder   |= bExpandIfValuesCloseToBorder;
148     m_bExpandWideValuesToZero        |= bExpandWideValuesToZero;
149     m_bExpandNarrowValuesTowardZero  |= bExpandNarrowValuesTowardZero;
150 
151     if( m_aSourceScale.AxisType==AxisType::PERCENT )
152         m_bExpandIfValuesCloseToBorder = false;
153 }
154 
155 void ScaleAutomatism::setMaximumAutoMainIncrementCount( sal_Int32 nMaximumAutoMainIncrementCount )
156 {
157     if( nMaximumAutoMainIncrementCount < 2 )
158         m_nMaximumAutoMainIncrementCount = 2; //#i82006
159     else if( nMaximumAutoMainIncrementCount > lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType ) )
160         m_nMaximumAutoMainIncrementCount = lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType );
161     else
162         m_nMaximumAutoMainIncrementCount = nMaximumAutoMainIncrementCount;
163 }
164 
165 void ScaleAutomatism::setAutomaticTimeResolution( sal_Int32 nTimeResolution )
166 {
167     m_nTimeResolution = nTimeResolution;
168 }
169 
170 void ScaleAutomatism::calculateExplicitScaleAndIncrement(
171         ExplicitScaleData& rExplicitScale, ExplicitIncrementData& rExplicitIncrement ) const
172 {
173     // fill explicit scale
174     rExplicitScale.Orientation = m_aSourceScale.Orientation;
175     rExplicitScale.Scaling = m_aSourceScale.Scaling;
176     rExplicitScale.AxisType = m_aSourceScale.AxisType;
177     rExplicitScale.NullDate = m_aNullDate;
178 
179     bool bAutoMinimum  = !(m_aSourceScale.Minimum >>= rExplicitScale.Minimum);
180     bool bAutoMaximum = !(m_aSourceScale.Maximum >>= rExplicitScale.Maximum);
181     bool bAutoOrigin = !(m_aSourceScale.Origin >>= rExplicitScale.Origin);
182 
183     // automatic scale minimum
184     if( bAutoMinimum )
185     {
186         if( m_aSourceScale.AxisType==AxisType::PERCENT )
187             rExplicitScale.Minimum = 0.0;
188         else if( ::rtl::math::isNan( m_fValueMinimum ) )
189         {
190             if( m_aSourceScale.AxisType==AxisType::DATE )
191                 rExplicitScale.Minimum = 36526.0; //1.1.2000
192             else
193                 rExplicitScale.Minimum = 0.0;   //@todo get Minimum from scaling or from plotter????
194         }
195         else
196             rExplicitScale.Minimum = m_fValueMinimum;
197     }
198 
199     // automatic scale maximum
200     if( bAutoMaximum )
201     {
202         if( m_aSourceScale.AxisType==AxisType::PERCENT )
203             rExplicitScale.Maximum = 1.0;
204         else if( ::rtl::math::isNan( m_fValueMaximum ) )
205         {
206             if( m_aSourceScale.AxisType==AxisType::DATE )
207                 rExplicitScale.Maximum = 40179.0; //1.1.2010
208             else
209                 rExplicitScale.Maximum = 10.0;  //@todo get Maximum from scaling or from plotter????
210         }
211         else
212             rExplicitScale.Maximum = m_fValueMaximum;
213     }
214 
215     //---------------------------------------------------------------
216     //fill explicit increment
217 
218     rExplicitScale.ShiftedCategoryPosition = m_aSourceScale.ShiftedCategoryPosition;
219     bool bIsLogarithm = false;
220 
221     //minimum and maximum of the ExplicitScaleData may be changed if allowed
222     if( m_aSourceScale.AxisType==AxisType::DATE )
223         calculateExplicitIncrementAndScaleForDateTimeAxis( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
224     else if( m_aSourceScale.AxisType==AxisType::CATEGORY || m_aSourceScale.AxisType==AxisType::SERIES )
225         calculateExplicitIncrementAndScaleForCategory( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
226     else
227     {
228         bIsLogarithm = AxisHelper::isLogarithmic( rExplicitScale.Scaling );
229         if( bIsLogarithm )
230             calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
231         else
232             calculateExplicitIncrementAndScaleForLinear( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
233     }
234 
235     // automatic origin
236     if( bAutoOrigin )
237     {
238         // #i71415# automatic origin for logarithmic axis
239         double fDefaulOrigin = bIsLogarithm ? 1.0 : 0.0;
240 
241         if( fDefaulOrigin < rExplicitScale.Minimum )
242             fDefaulOrigin = rExplicitScale.Minimum;
243         else if( fDefaulOrigin > rExplicitScale.Maximum )
244             fDefaulOrigin = rExplicitScale.Maximum;
245 
246         rExplicitScale.Origin = fDefaulOrigin;
247     }
248 }
249 
250 ScaleData ScaleAutomatism::getScale() const
251 {
252     return m_aSourceScale;
253 }
254 
255 Date ScaleAutomatism::getNullDate() const
256 {
257     return m_aNullDate;
258 }
259 
260 // private --------------------------------------------------------------------
261 
262 void ScaleAutomatism::calculateExplicitIncrementAndScaleForCategory(
263         ExplicitScaleData& rExplicitScale,
264         ExplicitIncrementData& rExplicitIncrement,
265         bool bAutoMinimum, bool bAutoMaximum ) const
266 {
267     // no scaling for categories
268     rExplicitScale.Scaling.clear();
269 
270     if( rExplicitScale.ShiftedCategoryPosition )
271         rExplicitScale.Maximum += 1.0;
272 
273     // ensure that at least one category is visible
274     if( rExplicitScale.Maximum <= rExplicitScale.Minimum )
275         rExplicitScale.Maximum = rExplicitScale.Minimum + 1.0;
276 
277     // default increment settings
278     rExplicitIncrement.PostEquidistant = sal_True;  // does not matter anyhow
279     rExplicitIncrement.Distance = 1.0;              // category axis always have a main increment of 1
280     rExplicitIncrement.BaseValue = 0.0;             // category axis always have a base of 0
281 
282     // automatic minimum and maximum
283     if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
284         rExplicitScale.Minimum = EquidistantTickFactory::getMinimumAtIncrement( rExplicitScale.Minimum, rExplicitIncrement );
285     if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
286         rExplicitScale.Maximum = EquidistantTickFactory::getMaximumAtIncrement( rExplicitScale.Maximum, rExplicitIncrement );
287 
288     //prevent performace killover
289     double fDistanceCount = ::rtl::math::approxFloor( (rExplicitScale.Maximum-rExplicitScale.Minimum) / rExplicitIncrement.Distance );
290     if( static_cast< sal_Int32 >( fDistanceCount ) > MAXIMUM_MANUAL_INCREMENT_COUNT )
291     {
292         double fMinimumFloor = ::rtl::math::approxFloor( rExplicitScale.Minimum );
293         double fMaximumCeil = ::rtl::math::approxCeil( rExplicitScale.Maximum );
294         rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / MAXIMUM_MANUAL_INCREMENT_COUNT );
295     }
296 
297     //---------------------------------------------------------------
298     //fill explicit sub increment
299     sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
300     for( sal_Int32 nN=0; nN<nSubCount; nN++ )
301     {
302         ExplicitSubIncrement aExplicitSubIncrement;
303         const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
304         if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
305         {
306             //scaling dependent
307             //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
308             aExplicitSubIncrement.IntervalCount = 2;
309         }
310         lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
311         if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
312         {
313             //scaling dependent
314             aExplicitSubIncrement.PostEquidistant = sal_False;
315         }
316         rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
317     }
318 }
319 
320 //-----------------------------------------------------------------------------------------
321 
322 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLogarithmic(
323         ExplicitScaleData& rExplicitScale,
324         ExplicitIncrementData& rExplicitIncrement,
325         bool bAutoMinimum, bool bAutoMaximum ) const
326 {
327     // *** STEP 1: initialize the range data ***
328 
329     const double fInputMinimum = rExplicitScale.Minimum;
330     const double fInputMaximum = rExplicitScale.Maximum;
331 
332     double fSourceMinimum = rExplicitScale.Minimum;
333     double fSourceMaximum = rExplicitScale.Maximum;
334 
335     // set automatic PostEquidistant to true (maybe scaling dependent?)
336     // Note: scaling with PostEquidistant==false is untested and needs review
337     if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
338         rExplicitIncrement.PostEquidistant = sal_True;
339 
340     /*  All following scaling code will operate on the logarithms of the source
341         values. In the last step, the original values will be restored. */
342     uno::Reference< XScaling > xScaling = rExplicitScale.Scaling;
343     if( !xScaling.is() )
344         xScaling.set( AxisHelper::createLogarithmicScaling() );
345     uno::Reference< XScaling > xInverseScaling = xScaling->getInverseScaling();
346 
347     fSourceMinimum = xScaling->doScaling( fSourceMinimum );
348     if( !::rtl::math::isFinite( fSourceMinimum ) )
349         fSourceMinimum = 0.0;
350     else if( ::rtl::math::approxEqual( fSourceMinimum, ::rtl::math::approxFloor( fSourceMinimum ) ) )
351         fSourceMinimum = ::rtl::math::approxFloor( fSourceMinimum );
352 
353     fSourceMaximum = xScaling->doScaling( fSourceMaximum );
354     if( !::rtl::math::isFinite( fSourceMaximum ) )
355         fSourceMaximum = 0.0;
356     else if( ::rtl::math::approxEqual( fSourceMaximum, ::rtl::math::approxFloor( fSourceMaximum ) ) )
357         fSourceMaximum = ::rtl::math::approxFloor( fSourceMaximum );
358 
359     /*  If range is invalid (minimum greater than maximum), change one of the
360         variable limits to validate the range. In this step, a zero-sized range
361         is still allowed. */
362     if( fSourceMinimum > fSourceMaximum )
363     {
364         // force changing the maximum, if both limits are fixed
365         if( bAutoMaximum || !bAutoMinimum )
366             fSourceMaximum = fSourceMinimum;
367         else
368             fSourceMinimum = fSourceMaximum;
369     }
370 
371     /*  If maximum is less than 0 (and therefore minimum too), minimum and
372         maximum will be negated and swapped to make the following algorithms
373         easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
374         [2,5], and the latter will be swapped back later. The range [0,0] is
375         explicitly excluded from swapping (this would result in [-1,0] instead
376         of the expected [0,1]). */
377     bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
378     if( bSwapAndNegateRange )
379     {
380         double fTempValue = fSourceMinimum;
381         fSourceMinimum = -fSourceMaximum;
382         fSourceMaximum = -fTempValue;
383         ::std::swap( bAutoMinimum, bAutoMaximum );
384     }
385 
386     // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
387 
388     double fTempMinimum = fSourceMinimum;
389     double fTempMaximum = fSourceMaximum;
390 
391     /*  If minimum is variable and greater than 0 (and therefore maximum too),
392         means all original values are greater than 1 (or all values are less
393         than 1, and the range has been swapped above), then: */
394     if( bAutoMinimum && (fTempMinimum > 0.0) )
395     {
396         /*  If minimum is less than 5 (i.e. original source values less than
397             B^5, B being the base of the scaling), or if minimum and maximum
398             are in different increment intervals (means, if minimum and maximum
399             are not both in the range [B^n,B^(n+1)] for a whole number n), set
400             minimum to 0, which results in B^0=1 on the axis. */
401         double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
402         double fMaximumFloor = ::rtl::math::approxFloor( fTempMaximum );
403         // handle the exact value B^(n+1) to be in the range [B^n,B^(n+1)]
404         if( ::rtl::math::approxEqual( fTempMaximum, fMaximumFloor ) )
405             fMaximumFloor -= 1.0;
406 
407         if( (fMinimumFloor < 5.0) || (fMinimumFloor < fMaximumFloor) )
408         {
409             if( m_bExpandWideValuesToZero )
410                 fTempMinimum = 0.0;
411         }
412         /*  Else (minimum and maximum are in one increment interval), expand
413             minimum toward 0 to make the 'shorter' data points visible. */
414         else
415         {
416             if( m_bExpandNarrowValuesTowardZero )
417                 fTempMinimum -= 1.0;
418         }
419     }
420 
421     /*  If range is still zero-sized (e.g. when minimum is fixed), set minimum
422         to 0, which makes the axis start/stop at the value 1. */
423     if( fTempMinimum == fTempMaximum )
424     {
425         if( bAutoMinimum && (fTempMaximum > 0.0) )
426             fTempMinimum = 0.0;
427         else
428             fTempMaximum += 1.0;    // always add one interval, even if maximum is fixed
429     }
430 
431     // *** STEP 3: calculate main interval size ***
432 
433     // base value (anchor position of the intervals), already scaled
434     if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
435     {
436         //scaling dependent
437         //@maybe todo is this default also plotter dependent ??
438         if( !bAutoMinimum )
439             rExplicitIncrement.BaseValue = fTempMinimum;
440         else if( !bAutoMaximum )
441             rExplicitIncrement.BaseValue = fTempMaximum;
442         else
443             rExplicitIncrement.BaseValue = 0.0;
444     }
445 
446     // calculate automatic interval
447     bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
448     if( bAutoDistance )
449         rExplicitIncrement.Distance = 0.0;
450 
451     /*  Restrict number of allowed intervals with user-defined distance to
452         MAXIMUM_MANUAL_INCREMENT_COUNT. */
453     sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
454         m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
455 
456     // repeat calculation until number of intervals are valid
457     bool bNeedIteration = true;
458     bool bHasCalculatedDistance = false;
459     while( bNeedIteration )
460     {
461         if( bAutoDistance )
462         {
463             // first iteration: calculate interval size from axis limits
464             if( !bHasCalculatedDistance )
465             {
466                 double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
467                 double fMaximumCeil = ::rtl::math::approxCeil( fTempMaximum );
468                 rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / nMaxMainIncrementCount );
469             }
470             else
471             {
472                 // following iterations: increase distance
473                 rExplicitIncrement.Distance += 1.0;
474             }
475 
476             // for next iteration: distance calculated -> use else path to increase
477             bHasCalculatedDistance = true;
478         }
479 
480         // *** STEP 4: additional space above or below the data points ***
481 
482         double fAxisMinimum = fTempMinimum;
483         double fAxisMaximum = fTempMaximum;
484 
485         // round to entire multiples of the distance and add additional space
486         if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
487         {
488             fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );
489 
490             //ensure valid values after scaling #i100995#
491             if( !bAutoDistance )
492             {
493                 double fCheck = xInverseScaling->doScaling( fAxisMinimum );
494                 if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 )
495                 {
496                     bAutoDistance = true;
497                     bHasCalculatedDistance = false;
498                     continue;
499                 }
500             }
501         }
502         if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
503         {
504             fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
505 
506             //ensure valid values after scaling #i100995#
507             if( !bAutoDistance )
508             {
509                 double fCheck = xInverseScaling->doScaling( fAxisMaximum );
510                 if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 )
511                 {
512                     bAutoDistance = true;
513                     bHasCalculatedDistance = false;
514                     continue;
515                 }
516             }
517         }
518 
519         // set the resulting limits (swap back to negative range if needed)
520         if( bSwapAndNegateRange )
521         {
522             rExplicitScale.Minimum = -fAxisMaximum;
523             rExplicitScale.Maximum = -fAxisMinimum;
524         }
525         else
526         {
527             rExplicitScale.Minimum = fAxisMinimum;
528             rExplicitScale.Maximum = fAxisMaximum;
529         }
530 
531         /*  If the number of intervals is too high (e.g. due to invalid fixed
532             distance or due to added space above or below data points),
533             calculate again with increased distance. */
534         double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
535         bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
536         // if manual distance is invalid, trigger automatic calculation
537         if( bNeedIteration )
538             bAutoDistance = true;
539 
540         // convert limits back to logarithmic scale
541         rExplicitScale.Minimum = xInverseScaling->doScaling( rExplicitScale.Minimum );
542         rExplicitScale.Maximum = xInverseScaling->doScaling( rExplicitScale.Maximum );
543 
544         //ensure valid values after scaling #i100995#
545         if( !::rtl::math::isFinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0)
546         {
547             rExplicitScale.Minimum = fInputMinimum;
548             if( !::rtl::math::isFinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0 )
549                 rExplicitScale.Minimum = 1.0;
550         }
551         if( !::rtl::math::isFinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
552         {
553             rExplicitScale.Maximum= fInputMaximum;
554             if( !::rtl::math::isFinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
555                 rExplicitScale.Maximum = 10.0;
556         }
557         if( rExplicitScale.Maximum < rExplicitScale.Minimum )
558             ::std::swap( rExplicitScale.Maximum, rExplicitScale.Minimum );
559     }
560 
561     //---------------------------------------------------------------
562     //fill explicit sub increment
563     sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
564     for( sal_Int32 nN=0; nN<nSubCount; nN++ )
565     {
566         ExplicitSubIncrement aExplicitSubIncrement;
567         const SubIncrement& rSubIncrement = m_aSourceScale.IncrementData.SubIncrements[nN];
568         if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
569         {
570             //scaling dependent
571             //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
572             aExplicitSubIncrement.IntervalCount = 9;
573         }
574         lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
575         if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
576         {
577             //scaling dependent
578             aExplicitSubIncrement.PostEquidistant = sal_False;
579         }
580         rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
581     }
582 }
583 
584 //-----------------------------------------------------------------------------------------
585 
586 void ScaleAutomatism::calculateExplicitIncrementAndScaleForDateTimeAxis(
587         ExplicitScaleData& rExplicitScale,
588         ExplicitIncrementData& rExplicitIncrement,
589         bool bAutoMinimum, bool bAutoMaximum ) const
590 {
591     Date aMinDate(m_aNullDate); aMinDate += static_cast<long>(::rtl::math::approxFloor(rExplicitScale.Minimum));
592     Date aMaxDate(m_aNullDate); aMaxDate += static_cast<long>(::rtl::math::approxFloor(rExplicitScale.Maximum));
593     rExplicitIncrement.PostEquidistant = sal_False;
594 
595     if( aMinDate > aMaxDate )
596     {
597         std::swap(aMinDate,aMaxDate);
598     }
599 
600     if( !(m_aSourceScale.TimeIncrement.TimeResolution >>= rExplicitScale.TimeResolution) )
601         rExplicitScale.TimeResolution = m_nTimeResolution;
602 
603     rExplicitScale.Scaling = new DateScaling(m_aNullDate,rExplicitScale.TimeResolution,false);
604 
605     // choose min and max suitable to time resolution
606     switch( rExplicitScale.TimeResolution )
607     {
608     case DAY:
609         if( rExplicitScale.ShiftedCategoryPosition )
610             aMaxDate++;//for explicit scales we need one interval more (maximum excluded)
611         break;
612     case MONTH:
613         aMinDate.SetDay(1);
614         aMaxDate.SetDay(1);
615         if( rExplicitScale.ShiftedCategoryPosition )
616             aMaxDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
617         if( DateHelper::IsLessThanOneMonthAway( aMinDate, aMaxDate ) )
618         {
619             if( bAutoMaximum || !bAutoMinimum )
620                 aMaxDate = DateHelper::GetDateSomeMonthsAway(aMinDate,1);
621             else
622                 aMinDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,-1);
623         }
624         break;
625     case YEAR:
626         aMinDate.SetDay(1);
627         aMinDate.SetMonth(1);
628         aMaxDate.SetDay(1);
629         aMaxDate.SetMonth(1);
630         if( rExplicitScale.ShiftedCategoryPosition )
631             aMaxDate = DateHelper::GetDateSomeYearsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
632         if( DateHelper::IsLessThanOneYearAway( aMinDate, aMaxDate ) )
633         {
634             if( bAutoMaximum || !bAutoMinimum )
635                 aMaxDate = DateHelper::GetDateSomeYearsAway(aMinDate,1);
636             else
637                 aMinDate = DateHelper::GetDateSomeYearsAway(aMaxDate,-1);
638         }
639         break;
640     }
641 
642     // set the resulting limits (swap back to negative range if needed)
643     rExplicitScale.Minimum = aMinDate - m_aNullDate;
644     rExplicitScale.Maximum = aMaxDate - m_aNullDate;
645 
646     bool bAutoMajor = !(m_aSourceScale.TimeIncrement.MajorTimeInterval >>= rExplicitIncrement.MajorTimeInterval);
647     bool bAutoMinor = !(m_aSourceScale.TimeIncrement.MinorTimeInterval >>= rExplicitIncrement.MinorTimeInterval);
648 
649     sal_Int32 nMaxMainIncrementCount = bAutoMajor ?
650         m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
651     if( nMaxMainIncrementCount > 1 )
652         nMaxMainIncrementCount--;
653 
654 
655     //choose major time interval:
656     long nDayCount = (aMaxDate-aMinDate);
657     long nMainIncrementCount = 1;
658     if( !bAutoMajor )
659     {
660         long nIntervalDayCount = rExplicitIncrement.MajorTimeInterval.Number;
661         if( rExplicitIncrement.MajorTimeInterval.TimeUnit < rExplicitScale.TimeResolution )
662             rExplicitIncrement.MajorTimeInterval.TimeUnit = rExplicitScale.TimeResolution;
663         switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
664         {
665         case DAY:
666             break;
667         case MONTH:
668             nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
669             break;
670         case YEAR:
671             nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
672             break;
673         }
674         nMainIncrementCount = nDayCount/nIntervalDayCount;
675         if( nMainIncrementCount > nMaxMainIncrementCount )
676             bAutoMajor = true;
677     }
678     if( bAutoMajor )
679     {
680         long nNumer = 1;
681         long nIntervalDays =  nDayCount / nMaxMainIncrementCount;
682         double nDaysPerInterval = 1.0;
683         if( nIntervalDays>365 || YEAR==rExplicitScale.TimeResolution )
684         {
685             rExplicitIncrement.MajorTimeInterval.TimeUnit = YEAR;
686             nDaysPerInterval = 365.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
687         }
688         else if( nIntervalDays>31 || MONTH==rExplicitScale.TimeResolution )
689         {
690             rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
691             nDaysPerInterval = 31.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
692         }
693         else
694         {
695             rExplicitIncrement.MajorTimeInterval.TimeUnit = DAY;
696             nDaysPerInterval = 1.0;
697         }
698 
699         nNumer = static_cast<sal_Int32>( rtl::math::approxCeil( nIntervalDays/nDaysPerInterval ) );
700         if(nNumer<=0)
701             nNumer=1;
702         if( rExplicitIncrement.MajorTimeInterval.TimeUnit == DAY )
703         {
704             if( nNumer>2 && nNumer<7 )
705                 nNumer=7;
706             else if( nNumer>7 )
707             {
708                 rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
709                 nDaysPerInterval = 31.0;
710                 nNumer = static_cast<sal_Int32>( rtl::math::approxCeil( nIntervalDays/nDaysPerInterval ) );
711                 if(nNumer<=0)
712                     nNumer=1;
713             }
714         }
715         rExplicitIncrement.MajorTimeInterval.Number = nNumer;
716         nMainIncrementCount = static_cast<long>(nDayCount/(nNumer*nDaysPerInterval));
717     }
718 
719     //choose minor time interval:
720     if( !bAutoMinor )
721     {
722         if( rExplicitIncrement.MinorTimeInterval.TimeUnit > rExplicitIncrement.MajorTimeInterval.TimeUnit )
723             rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
724         long nIntervalDayCount = rExplicitIncrement.MinorTimeInterval.Number;
725         switch( rExplicitIncrement.MinorTimeInterval.TimeUnit )
726         {
727         case DAY:
728             break;
729         case MONTH:
730             nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
731             break;
732         case YEAR:
733             nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
734             break;
735         }
736         if( nDayCount/nIntervalDayCount > nMaxMainIncrementCount )
737             bAutoMinor = true;
738     }
739     if( bAutoMinor )
740     {
741         rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
742         rExplicitIncrement.MinorTimeInterval.Number = 1;
743         if( nMainIncrementCount > 100 )
744             rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
745         else
746         {
747             if( rExplicitIncrement.MajorTimeInterval.Number >= 2 )
748             {
749                 if( !(rExplicitIncrement.MajorTimeInterval.Number%2) )
750                     rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/2;
751                 else if( !(rExplicitIncrement.MajorTimeInterval.Number%3) )
752                     rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/3;
753                 else if( !(rExplicitIncrement.MajorTimeInterval.Number%5) )
754                     rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/5;
755                 else if( rExplicitIncrement.MajorTimeInterval.Number > 50 )
756                     rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
757             }
758             else
759             {
760                 switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
761                 {
762                     case DAY:
763                         break;
764                     case MONTH:
765                         if( rExplicitScale.TimeResolution == DAY )
766                             rExplicitIncrement.MinorTimeInterval.TimeUnit = DAY;
767                         break;
768                     case YEAR:
769                         if( rExplicitScale.TimeResolution <= MONTH )
770                             rExplicitIncrement.MinorTimeInterval.TimeUnit = MONTH;
771                         break;
772                 }
773             }
774         }
775     }
776 
777 }
778 
779 //-----------------------------------------------------------------------------------------
780 
781 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLinear(
782         ExplicitScaleData& rExplicitScale,
783         ExplicitIncrementData& rExplicitIncrement,
784         bool bAutoMinimum, bool bAutoMaximum ) const
785 {
786     // *** STEP 1: initialize the range data ***
787 
788     double fSourceMinimum = rExplicitScale.Minimum;
789     double fSourceMaximum = rExplicitScale.Maximum;
790 
791     // set automatic PostEquidistant to true (maybe scaling dependent?)
792     if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
793         rExplicitIncrement.PostEquidistant = sal_True;
794 
795     /*  If range is invalid (minimum greater than maximum), change one of the
796         variable limits to validate the range. In this step, a zero-sized range
797         is still allowed. */
798     if( fSourceMinimum > fSourceMaximum )
799     {
800         // force changing the maximum, if both limits are fixed
801         if( bAutoMaximum || !bAutoMinimum )
802             fSourceMaximum = fSourceMinimum;
803         else
804             fSourceMinimum = fSourceMaximum;
805     }
806 
807     /*  If maximum is zero or negative (and therefore minimum too), minimum and
808         maximum will be negated and swapped to make the following algorithms
809         easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
810         [2,5], and the latter will be swapped back later. The range [0,0] is
811         explicitly excluded from swapping (this would result in [-1,0] instead
812         of the expected [0,1]). */
813     bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
814     if( bSwapAndNegateRange )
815     {
816         double fTempValue = fSourceMinimum;
817         fSourceMinimum = -fSourceMaximum;
818         fSourceMaximum = -fTempValue;
819         ::std::swap( bAutoMinimum, bAutoMaximum );
820     }
821 
822     // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
823 
824     double fTempMinimum = fSourceMinimum;
825     double fTempMaximum = fSourceMaximum;
826 
827     /*  If minimum is variable and greater than 0 (and therefore maximum too),
828         means all values are positive (or all values are negative, and the
829         range has been swapped above), then: */
830     if( bAutoMinimum && (fTempMinimum > 0.0) )
831     {
832         /*  If minimum equals maximum, or if minimum is less than 5/6 of
833             maximum, set minimum to 0. */
834         if( (fTempMinimum == fTempMaximum) || (fTempMinimum / fTempMaximum < 5.0 / 6.0) )
835         {
836             if( m_bExpandWideValuesToZero )
837                 fTempMinimum = 0.0;
838         }
839         /*  Else (minimum is greater than or equal to 5/6 of maximum), add half
840             of the visible range (expand minimum toward 0) to make the
841             'shorter' data points visible. */
842         else
843         {
844             if( m_bExpandNarrowValuesTowardZero )
845                 fTempMinimum -= (fTempMaximum - fTempMinimum) / 2.0;
846         }
847     }
848 
849     /*  If range is still zero-sized (e.g. when minimum is fixed), add some
850         space to a variable limit. */
851     if( fTempMinimum == fTempMaximum )
852     {
853         if( bAutoMaximum || !bAutoMinimum )
854         {
855             // change 0 to 1, otherwise double the value
856             if( fTempMaximum == 0.0 )
857                 fTempMaximum = 1.0;
858             else
859                 fTempMaximum *= 2.0;
860         }
861         else
862         {
863             // change 0 to -1, otherwise halve the value
864             if( fTempMinimum == 0.0 )
865                 fTempMinimum = -1.0;
866             else
867                 fTempMinimum /= 2.0;
868         }
869     }
870 
871     // *** STEP 3: calculate main interval size ***
872 
873     // base value (anchor position of the intervals)
874     if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
875     {
876         if( !bAutoMinimum )
877             rExplicitIncrement.BaseValue = fTempMinimum;
878         else if( !bAutoMaximum )
879             rExplicitIncrement.BaseValue = fTempMaximum;
880         else
881             rExplicitIncrement.BaseValue = 0.0;
882     }
883 
884     // calculate automatic interval
885     bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
886     /*  Restrict number of allowed intervals with user-defined distance to
887         MAXIMUM_MANUAL_INCREMENT_COUNT. */
888     sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
889         m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
890 
891     double fDistanceMagnitude = 0.0;
892     double fDistanceNormalized = 0.0;
893     bool bHasNormalizedDistance = false;
894 
895     // repeat calculation until number of intervals are valid
896     bool bNeedIteration = true;
897     while( bNeedIteration )
898     {
899         if( bAutoDistance )
900         {
901             // first iteration: calculate interval size from axis limits
902             if( !bHasNormalizedDistance )
903             {
904                 // raw size of an interval
905                 double fDistance = (fTempMaximum - fTempMinimum) / nMaxMainIncrementCount;
906 
907                 // if distance of is less than 1e-307, do not do anything
908                 if( fDistance <= 1.0e-307 )
909                 {
910                     fDistanceNormalized = 1.0;
911                     fDistanceMagnitude = 1.0e-307;
912                 }
913                 else
914                 {
915                     // distance magnitude (a power of 10)
916                     int nExponent = static_cast< int >( ::rtl::math::approxFloor( log10( fDistance ) ) );
917                     fDistanceMagnitude = ::rtl::math::pow10Exp( 1.0, nExponent );
918 
919                     // stick normalized distance to a few predefined values
920                     fDistanceNormalized = fDistance / fDistanceMagnitude;
921                     if( fDistanceNormalized <= 1.0 )
922                         fDistanceNormalized = 1.0;
923                     else if( fDistanceNormalized <= 2.0 )
924                         fDistanceNormalized = 2.0;
925                     else if( fDistanceNormalized <= 5.0 )
926                         fDistanceNormalized = 5.0;
927                     else
928                     {
929                         fDistanceNormalized = 1.0;
930                         fDistanceMagnitude *= 10;
931                     }
932                 }
933                 // for next iteration: distance is normalized -> use else path to increase distance
934                 bHasNormalizedDistance = true;
935             }
936             // following iterations: increase distance, use only allowed values
937             else
938             {
939                 if( fDistanceNormalized == 1.0 )
940                     fDistanceNormalized = 2.0;
941                 else if( fDistanceNormalized == 2.0 )
942                     fDistanceNormalized = 5.0;
943                 else
944                 {
945                     fDistanceNormalized = 1.0;
946                     fDistanceMagnitude *= 10;
947                 }
948             }
949 
950             // set the resulting distance
951             rExplicitIncrement.Distance = fDistanceNormalized * fDistanceMagnitude;
952         }
953 
954         // *** STEP 4: additional space above or below the data points ***
955 
956         double fAxisMinimum = fTempMinimum;
957         double fAxisMaximum = fTempMaximum;
958 
959         // round to entire multiples of the distance and add additional space
960         if( bAutoMinimum )
961         {
962             // round to entire multiples of the distance, based on the base value
963             if( m_bExpandBorderToIncrementRhythm )
964                 fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );
965             // additional space, if source minimum is to near at axis minimum
966             if( m_bExpandIfValuesCloseToBorder )
967                 if( (fAxisMinimum != 0.0) && ((fAxisMaximum - fSourceMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
968                     fAxisMinimum -= rExplicitIncrement.Distance;
969         }
970         if( bAutoMaximum )
971         {
972             // round to entire multiples of the distance, based on the base value
973             if( m_bExpandBorderToIncrementRhythm )
974                 fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
975             // additional space, if source maximum is to near at axis maximum
976             if( m_bExpandIfValuesCloseToBorder )
977                 if( (fAxisMaximum != 0.0) && ((fSourceMaximum - fAxisMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
978                     fAxisMaximum += rExplicitIncrement.Distance;
979         }
980 
981         // set the resulting limits (swap back to negative range if needed)
982         if( bSwapAndNegateRange )
983         {
984             rExplicitScale.Minimum = -fAxisMaximum;
985             rExplicitScale.Maximum = -fAxisMinimum;
986         }
987         else
988         {
989             rExplicitScale.Minimum = fAxisMinimum;
990             rExplicitScale.Maximum = fAxisMaximum;
991         }
992 
993         /*  If the number of intervals is too high (e.g. due to invalid fixed
994             distance or due to added space above or below data points),
995             calculate again with increased distance. */
996         double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
997         bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
998         // if manual distance is invalid, trigger automatic calculation
999         if( bNeedIteration )
1000             bAutoDistance = true;
1001     }
1002 
1003     //---------------------------------------------------------------
1004     //fill explicit sub increment
1005     sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
1006     for( sal_Int32 nN=0; nN<nSubCount; nN++ )
1007     {
1008         ExplicitSubIncrement aExplicitSubIncrement;
1009         const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
1010         if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
1011         {
1012             //scaling dependent
1013             //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
1014             aExplicitSubIncrement.IntervalCount = 2;
1015         }
1016         lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
1017         if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
1018         {
1019             //scaling dependent
1020             aExplicitSubIncrement.PostEquidistant = sal_False;
1021         }
1022         rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
1023     }
1024 }
1025 
1026 //.............................................................................
1027 } //namespace chart
1028 //.............................................................................
1029