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

Generated by: LCOV version 1.11