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