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