LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/chart2/source/view/axes - ScaleAutomatism.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 282 468 60.3 %
Date: 2013-07-09 Functions: 15 19 78.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10