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