Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/chart2/source/view/axes/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 40 kB image not shown  

Quelle  ScaleAutomatism.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <ScaleAutomatism.hxx>
#include "Tickmarks_Equidistant.hxx"
#include <DateHelper.hxx>
#include "DateScaling.hxx"
#include <AxisHelper.hxx>
#include <com/sun/star/chart/TimeUnit.hpp>
#include <com/sun/star/chart2/AxisType.hpp>

#include <rtl/math.hxx>
#include <tools/long.hxx>
#include <limits>

namespace chart
{
using namespace ::com::sun::star;
using namespace ::com::sun::star::chart2;
using ::com::sun::star::chart::TimeUnit::DAY;
using ::com::sun::star::chart::TimeUnit::MONTH;
using ::com::sun::star::chart::TimeUnit::YEAR;

const sal_Int32 MAXIMUM_MANUAL_INCREMENT_COUNT = 500;
const sal_Int32 MAXIMUM_SUB_INCREMENT_COUNT = 100;

static sal_Int32 lcl_getMaximumAutoIncrementCount( sal_Int32 nAxisType )
{
    sal_Int32 nMaximumAutoIncrementCount = 10;
    if( nAxisType==AxisType::DATE )
        nMaximumAutoIncrementCount = MAXIMUM_MANUAL_INCREMENT_COUNT;
    return nMaximumAutoIncrementCount;
}

namespace
{

void lcl_ensureMaximumSubIncrementCount( sal_Int32& rnSubIntervalCount )
{
    if( rnSubIntervalCount > MAXIMUM_SUB_INCREMENT_COUNT )
        rnSubIntervalCount = MAXIMUM_SUB_INCREMENT_COUNT;
}

}//end anonymous namespace

ExplicitScaleData::ExplicitScaleData()
    : Minimum(0.0)
    , Maximum(10.0)
    , Origin(0.0)
    , Orientation(css::chart2::AxisOrientation_MATHEMATICAL)
    , AxisType(css::chart2::AxisType::REALNUMBER)
    , m_bShiftedCategoryPosition(false)
    , TimeResolution(css::chart::TimeUnit::DAY)
    , NullDate(30,12,1899)
{
}

ExplicitSubIncrement::ExplicitSubIncrement()
    : IntervalCount(2)
    , PostEquidistant(true)
{
}

ExplicitIncrementData::ExplicitIncrementData()
    : MajorTimeInterval(1,css::chart::TimeUnit::DAY)
    , MinorTimeInterval(1,css::chart::TimeUnit::DAY)
    , Distance(1.0)
    , PostEquidistant(true)
    , BaseValue(0.0)
{
}

ScaleAutomatism::ScaleAutomatism( const ScaleData& rSourceScale, const Date& rNullDate )
                    : m_aSourceScale( rSourceScale )
                    , m_fValueMinimum( 0.0 )
                    , m_fValueMaximum( 0.0 )
                    , m_nMaximumAutoMainIncrementCount( lcl_getMaximumAutoIncrementCount( rSourceScale.AxisType ) )
                    , m_bExpandBorderToIncrementRhythm( false )
                    , m_bExpandIfValuesCloseToBorder( false )
                    , m_bExpandWideValuesToZero( false )
                    , m_bExpandNarrowValuesTowardZero( false )
                    , m_nTimeResolution(css::chart::TimeUnit::DAY)
                    , m_aNullDate(rNullDate)
{
    resetValueRange();

    double fExplicitOrigin = 0.0;
    if( m_aSourceScale.Origin >>= fExplicitOrigin )
        expandValueRange( fExplicitOrigin, fExplicitOrigin);
}

void ScaleAutomatism::resetValueRange( )
{
    m_fValueMinimum = std::numeric_limits<double>::quiet_NaN();
    m_fValueMaximum = std::numeric_limits<double>::quiet_NaN();
}

void ScaleAutomatism::expandValueRange( double fMinimum, double fMaximum )
{
    // if m_fValueMinimum and m_fValueMaximum == 0, it means that they were not determined.
    // m_fValueMinimum == 0 makes impossible to determine real minimum,
    // so they need to be reset tdf#96807
    if( (m_fValueMinimum == 0.0) && (m_fValueMaximum == 0.0) )
        resetValueRange();
    if( (fMinimum < m_fValueMinimum) || std::isnan( m_fValueMinimum ) )
        m_fValueMinimum = fMinimum;
    if( (fMaximum > m_fValueMaximum) || std::isnan( m_fValueMaximum ) )
        m_fValueMaximum = fMaximum;
}

void ScaleAutomatism::setAutoScalingOptions(
        bool bExpandBorderToIncrementRhythm,
        bool bExpandIfValuesCloseToBorder,
        bool bExpandWideValuesToZero,
        bool bExpandNarrowValuesTowardZero )
{
    // if called multiple times, enable an option, if it is set in at least one call
    m_bExpandBorderToIncrementRhythm |= bExpandBorderToIncrementRhythm;
    m_bExpandIfValuesCloseToBorder   |= bExpandIfValuesCloseToBorder;
    m_bExpandWideValuesToZero        |= bExpandWideValuesToZero;
    m_bExpandNarrowValuesTowardZero  |= bExpandNarrowValuesTowardZero;

    if( m_aSourceScale.AxisType==AxisType::PERCENT )
        m_bExpandIfValuesCloseToBorder = false;
}

void ScaleAutomatism::setMaximumAutoMainIncrementCount( sal_Int32 nMaximumAutoMainIncrementCount )
{
    if( nMaximumAutoMainIncrementCount < 2 )
        m_nMaximumAutoMainIncrementCount = 2; //#i82006
    else if( nMaximumAutoMainIncrementCount > lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType ) )
        m_nMaximumAutoMainIncrementCount = lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType );
    else
        m_nMaximumAutoMainIncrementCount = nMaximumAutoMainIncrementCount;
}

void ScaleAutomatism::setAutomaticTimeResolution( sal_Int32 nTimeResolution )
{
    m_nTimeResolution = nTimeResolution;
}

void ScaleAutomatism::calculateExplicitScaleAndIncrement(
        ExplicitScaleData& rExplicitScale, ExplicitIncrementData& rExplicitIncrement ) const
{
    // fill explicit scale
    rExplicitScale.Orientation = m_aSourceScale.Orientation;
    rExplicitScale.Scaling = m_aSourceScale.Scaling;
    rExplicitScale.AxisType = m_aSourceScale.AxisType;
    rExplicitScale.NullDate = m_aNullDate;

    bool bAutoMinimum  = !(m_aSourceScale.Minimum >>= rExplicitScale.Minimum);
    bool bAutoMaximum = !(m_aSourceScale.Maximum >>= rExplicitScale.Maximum);
    bool bAutoOrigin = !(m_aSourceScale.Origin >>= rExplicitScale.Origin);

    // automatic scale minimum
    if( bAutoMinimum )
    {
        if( m_aSourceScale.AxisType==AxisType::PERCENT )
            rExplicitScale.Minimum = 0.0;
        else if( std::isnan( m_fValueMinimum ) )
        {
            if( m_aSourceScale.AxisType==AxisType::DATE )
                rExplicitScale.Minimum = 36526.0; //1.1.2000
            else
                rExplicitScale.Minimum = 0.0;   //@todo get Minimum from scaling or from plotter????
        }
        else
            rExplicitScale.Minimum = m_fValueMinimum;
    }

    // automatic scale maximum
    if( bAutoMaximum )
    {
        if( m_aSourceScale.AxisType==AxisType::PERCENT )
            rExplicitScale.Maximum = 1.0;
        else if( std::isnan( m_fValueMaximum ) )
        {
            if( m_aSourceScale.AxisType==AxisType::DATE )
                rExplicitScale.Maximum = 40179.0; //1.1.2010
            else
                rExplicitScale.Maximum = 10.0;  //@todo get Maximum from scaling or from plotter????
        }
        else
            rExplicitScale.Maximum = m_fValueMaximum;
    }

    //fill explicit increment

    rExplicitScale.m_bShiftedCategoryPosition = m_aSourceScale.ShiftedCategoryPosition;
    bool bIsLogarithm = false;

    //minimum and maximum of the ExplicitScaleData may be changed if allowed
    if( m_aSourceScale.AxisType==AxisType::DATE )
        calculateExplicitIncrementAndScaleForDateTimeAxis( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
    else if( m_aSourceScale.AxisType==AxisType::CATEGORY || m_aSourceScale.AxisType==AxisType::SERIES )
        calculateExplicitIncrementAndScaleForCategory( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
    else
    {
        bIsLogarithm = AxisHelper::isLogarithmic( rExplicitScale.Scaling );
        if( bIsLogarithm )
            calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
        else
            calculateExplicitIncrementAndScaleForLinear( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
    }

    // automatic origin
    if( bAutoOrigin )
    {
        // #i71415# automatic origin for logarithmic axis
        double fDefaulOrigin = bIsLogarithm ? 1.0 : 0.0;

        if( fDefaulOrigin < rExplicitScale.Minimum )
            fDefaulOrigin = rExplicitScale.Minimum;
        else if( fDefaulOrigin > rExplicitScale.Maximum )
            fDefaulOrigin = rExplicitScale.Maximum;

        rExplicitScale.Origin = fDefaulOrigin;
    }
}

void ScaleAutomatism::calculateExplicitIncrementAndScaleForCategory(
        ExplicitScaleData& rExplicitScale,
        ExplicitIncrementData& rExplicitIncrement,
        bool bAutoMinimum, bool bAutoMaximum ) const
{
    // no scaling for categories
    rExplicitScale.Scaling.clear();

    if( rExplicitScale.m_bShiftedCategoryPosition )
        rExplicitScale.Maximum += 1.0;

    // ensure that at least one category is visible
    if( rExplicitScale.Maximum <= rExplicitScale.Minimum )
        rExplicitScale.Maximum = rExplicitScale.Minimum + 1.0;

    // default increment settings
    rExplicitIncrement.PostEquidistant = true;  // does not matter anyhow
    rExplicitIncrement.Distance = 1.0;              // category axis always have a main increment of 1
    rExplicitIncrement.BaseValue = 0.0;             // category axis always have a base of 0

    // automatic minimum and maximum
    if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
        rExplicitScale.Minimum = EquidistantTickFactory::getMinimumAtIncrement( rExplicitScale.Minimum, rExplicitIncrement );
    if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
        rExplicitScale.Maximum = EquidistantTickFactory::getMaximumAtIncrement( rExplicitScale.Maximum, rExplicitIncrement );

    //prevent performance killover
    double fDistanceCount = ::rtl::math::approxFloor( (rExplicitScale.Maximum-rExplicitScale.Minimum) / rExplicitIncrement.Distance );
    ifstatic_cast< sal_Int32 >( fDistanceCount ) > MAXIMUM_MANUAL_INCREMENT_COUNT )
    {
        double fMinimumFloor = ::rtl::math::approxFloor( rExplicitScale.Minimum );
        double fMaximumCeil = ::rtl::math::approxCeil( rExplicitScale.Maximum );
        rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / MAXIMUM_MANUAL_INCREMENT_COUNT );
    }

    //fill explicit sub increment
    sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
    for( sal_Int32 nN=0; nN<nSubCount; nN++ )
    {
        ExplicitSubIncrement aExplicitSubIncrement;
        const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
        if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
        {
            //scaling dependent
            //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
            aExplicitSubIncrement.IntervalCount = 2;
        }
        lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
        if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
        {
            //scaling dependent
            aExplicitSubIncrement.PostEquidistant = false;
        }
        rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
    }
}

void ScaleAutomatism::calculateExplicitIncrementAndScaleForLogarithmic(
        ExplicitScaleData& rExplicitScale,
        ExplicitIncrementData& rExplicitIncrement,
        bool bAutoMinimum, bool bAutoMaximum ) const
{
    // *** STEP 1: initialize the range data ***

    const double fInputMinimum = rExplicitScale.Minimum;
    const double fInputMaximum = rExplicitScale.Maximum;

    double fSourceMinimum = rExplicitScale.Minimum;
    double fSourceMaximum = rExplicitScale.Maximum;

    // set automatic PostEquidistant to true (maybe scaling dependent?)
    // Note: scaling with PostEquidistant==false is untested and needs review
    if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
        rExplicitIncrement.PostEquidistant = true;

    /*  All following scaling code will operate on the logarithms of the source
        values. In the last step, the original values will be restored. */

    uno::Reference< XScaling > xScaling = rExplicitScale.Scaling;
    if( !xScaling.is() )
        xScaling.set( AxisHelper::createLogarithmicScaling() );
    uno::Reference< XScaling > xInverseScaling = xScaling->getInverseScaling();

    fSourceMinimum = xScaling->doScaling( fSourceMinimum );
    if( !std::isfinite( fSourceMinimum ) )
        fSourceMinimum = 0.0;
    else if( ::rtl::math::approxEqual( fSourceMinimum, ::rtl::math::approxFloor( fSourceMinimum ) ) )
        fSourceMinimum = ::rtl::math::approxFloor( fSourceMinimum );

    fSourceMaximum = xScaling->doScaling( fSourceMaximum );
    if( !std::isfinite( fSourceMaximum ) )
        fSourceMaximum = 0.0;
    else if( ::rtl::math::approxEqual( fSourceMaximum, ::rtl::math::approxFloor( fSourceMaximum ) ) )
        fSourceMaximum = ::rtl::math::approxFloor( fSourceMaximum );

    /*  If range is invalid (minimum greater than maximum), change one of the
        variable limits to validate the range. In this step, a zero-sized range
        is still allowed. */

    if( fSourceMinimum > fSourceMaximum )
    {
        // force changing the maximum, if both limits are fixed
        if( bAutoMaximum || !bAutoMinimum )
            fSourceMaximum = fSourceMinimum;
        else
            fSourceMinimum = fSourceMaximum;
    }

    /*  If maximum is less than 0 (and therefore minimum too), minimum and
        maximum will be negated and swapped to make the following algorithms
        easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
        [2,5], and the latter will be swapped back later. The range [0,0] is
        explicitly excluded from swapping (this would result in [-1,0] instead
        of the expected [0,1]). */

    bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
    if( bSwapAndNegateRange )
    {
        double fTempValue = fSourceMinimum;
        fSourceMinimum = -fSourceMaximum;
        fSourceMaximum = -fTempValue;
        std::swap( bAutoMinimum, bAutoMaximum );
    }

    // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***

    double fTempMinimum = fSourceMinimum;
    double fTempMaximum = fSourceMaximum;

    /*  If minimum is variable and greater than 0 (and therefore maximum too),
        means all original values are greater than 1 (or all values are less
        than 1, and the range has been swapped above), then: */

    if( bAutoMinimum && (fTempMinimum > 0.0) )
    {
        double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
        double fMaximumFloor = ::rtl::math::approxFloor( fTempMaximum );
        // handle the exact value B^(n+1) to be in the range [B^n,B^(n+1)]
        if( ::rtl::math::approxEqual( fTempMaximum, fMaximumFloor ) )
            fMaximumFloor -= 1.0;

        if( fMinimumFloor == fMaximumFloor )
        {
        /*  if minimum and maximum are in one increment interval, expand
            minimum toward 0 to make the 'shorter' data points visible. */

            if( m_bExpandNarrowValuesTowardZero )
                fTempMinimum -= 1.0;
        }
    }

    /*  If range is still zero-sized (e.g. when minimum is fixed), set minimum
        to 0, which makes the axis start/stop at the value 1. */

    if( fTempMinimum == fTempMaximum )
    {
        if( bAutoMinimum && (fTempMaximum > 0.0) )
            fTempMinimum = 0.0;
        else
            fTempMaximum += 1.0;    // always add one interval, even if maximum is fixed
    }

    // *** STEP 3: calculate main interval size ***

    // base value (anchor position of the intervals), already scaled
    if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
    {
        //scaling dependent
        //@maybe todo is this default also plotter dependent ??
        if( !bAutoMinimum )
            rExplicitIncrement.BaseValue = fTempMinimum;
        else if( !bAutoMaximum )
            rExplicitIncrement.BaseValue = fTempMaximum;
        else
            rExplicitIncrement.BaseValue = 0.0;
    }

    // calculate automatic interval
    bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
    if( bAutoDistance )
        rExplicitIncrement.Distance = 0.0;

    /*  Restrict number of allowed intervals with user-defined distance to
        MAXIMUM_MANUAL_INCREMENT_COUNT. */

    sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
        m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;

    // repeat calculation until number of intervals are valid
    bool bNeedIteration = true;
    bool bHasCalculatedDistance = false;
    while( bNeedIteration )
    {
        if( bAutoDistance )
        {
            // first iteration: calculate interval size from axis limits
            if( !bHasCalculatedDistance )
            {
                double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
                double fMaximumCeil = ::rtl::math::approxCeil( fTempMaximum );
                rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / nMaxMainIncrementCount );
            }
            else
            {
                // following iterations: increase distance
                rExplicitIncrement.Distance += 1.0;
            }

            // for next iteration: distance calculated -> use else path to increase
            bHasCalculatedDistance = true;
        }

        // *** STEP 4: additional space above or below the data points ***

        double fAxisMinimum = fTempMinimum;
        double fAxisMaximum = fTempMaximum;

        // round to entire multiples of the distance and add additional space
        if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
        {
            fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );

            //ensure valid values after scaling #i100995#
            if( !bAutoDistance )
            {
                double fCheck = xInverseScaling->doScaling( fAxisMinimum );
                if( !std::isfinite( fCheck ) || fCheck <= 0 )
                {
                    bAutoDistance = true;
                    bHasCalculatedDistance = false;
                    continue;
                }
            }
        }
        if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
        {
            fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );

            //ensure valid values after scaling #i100995#
            if( !bAutoDistance )
            {
                double fCheck = xInverseScaling->doScaling( fAxisMaximum );
                if( !std::isfinite( fCheck ) || fCheck <= 0 )
                {
                    bAutoDistance = true;
                    bHasCalculatedDistance = false;
                    continue;
                }
            }
        }

        // set the resulting limits (swap back to negative range if needed)
        if( bSwapAndNegateRange )
        {
            rExplicitScale.Minimum = -fAxisMaximum;
            rExplicitScale.Maximum = -fAxisMinimum;
        }
        else
        {
            rExplicitScale.Minimum = fAxisMinimum;
            rExplicitScale.Maximum = fAxisMaximum;
        }

        /*  If the number of intervals is too high (e.g. due to invalid fixed
            distance or due to added space above or below data points),
            calculate again with increased distance. */

        double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
        bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
        // if manual distance is invalid, trigger automatic calculation
        if( bNeedIteration )
            bAutoDistance = true;

        // convert limits back to logarithmic scale
        rExplicitScale.Minimum = xInverseScaling->doScaling( rExplicitScale.Minimum );
        rExplicitScale.Maximum = xInverseScaling->doScaling( rExplicitScale.Maximum );

        //ensure valid values after scaling #i100995#
        if( !std::isfinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0)
        {
            rExplicitScale.Minimum = fInputMinimum;
            if( !std::isfinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0 )
                rExplicitScale.Minimum = 1.0;
        }
        if( !std::isfinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
        {
            rExplicitScale.Maximum= fInputMaximum;
            if( !std::isfinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
                rExplicitScale.Maximum = 10.0;
        }
        if( rExplicitScale.Maximum < rExplicitScale.Minimum )
            std::swap( rExplicitScale.Maximum, rExplicitScale.Minimum );
    }

    //fill explicit sub increment
    sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
    for( sal_Int32 nN=0; nN<nSubCount; nN++ )
    {
        ExplicitSubIncrement aExplicitSubIncrement;
        const SubIncrement& rSubIncrement = m_aSourceScale.IncrementData.SubIncrements[nN];
        if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
        {
            //scaling dependent
            //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
            aExplicitSubIncrement.IntervalCount = 9;
        }
        lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
        if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
        {
            //scaling dependent
            aExplicitSubIncrement.PostEquidistant = false;
        }
        rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
    }
}

void ScaleAutomatism::calculateExplicitIncrementAndScaleForDateTimeAxis(
        ExplicitScaleData& rExplicitScale,
        ExplicitIncrementData& rExplicitIncrement,
        bool bAutoMinimum, bool bAutoMaximum ) const
{
    Date aMinDate(m_aNullDate); aMinDate.AddDays(::rtl::math::approxFloor(rExplicitScale.Minimum));
    Date aMaxDate(m_aNullDate); aMaxDate.AddDays(::rtl::math::approxFloor(rExplicitScale.Maximum));
    rExplicitIncrement.PostEquidistant = false;

    if( aMinDate > aMaxDate )
    {
        std::swap(aMinDate,aMaxDate);
    }

    if( !(m_aSourceScale.TimeIncrement.TimeResolution >>= rExplicitScale.TimeResolution) )
        rExplicitScale.TimeResolution = m_nTimeResolution;

    rExplicitScale.Scaling = new DateScaling(m_aNullDate,rExplicitScale.TimeResolution,false);

    // choose min and max suitable to time resolution
    switch( rExplicitScale.TimeResolution )
    {
    case DAY:
        if( rExplicitScale.m_bShiftedCategoryPosition )
            ++aMaxDate; //for explicit scales we need one interval more (maximum excluded)
        break;
    case MONTH:
        aMinDate.SetDay(1);
        aMaxDate.SetDay(1);
        if( rExplicitScale.m_bShiftedCategoryPosition )
            aMaxDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
        if( DateHelper::IsLessThanOneMonthAway( aMinDate, aMaxDate ) )
        {
            if( bAutoMaximum || !bAutoMinimum )
                aMaxDate = DateHelper::GetDateSomeMonthsAway(aMinDate,1);
            else
                aMinDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,-1);
        }
        break;
    case YEAR:
        aMinDate.SetDay(1);
        aMinDate.SetMonth(1);
        aMaxDate.SetDay(1);
        aMaxDate.SetMonth(1);
        if( rExplicitScale.m_bShiftedCategoryPosition )
            aMaxDate = DateHelper::GetDateSomeYearsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
        if( DateHelper::IsLessThanOneYearAway( aMinDate, aMaxDate ) )
        {
            if( bAutoMaximum || !bAutoMinimum )
                aMaxDate = DateHelper::GetDateSomeYearsAway(aMinDate,1);
            else
                aMinDate = DateHelper::GetDateSomeYearsAway(aMaxDate,-1);
        }
        break;
    }

    // set the resulting limits (swap back to negative range if needed)
    rExplicitScale.Minimum = aMinDate - m_aNullDate;
    rExplicitScale.Maximum = aMaxDate - m_aNullDate;

    bool bAutoMajor = !(m_aSourceScale.TimeIncrement.MajorTimeInterval >>= rExplicitIncrement.MajorTimeInterval);
    bool bAutoMinor = !(m_aSourceScale.TimeIncrement.MinorTimeInterval >>= rExplicitIncrement.MinorTimeInterval);

    sal_Int32 nMaxMainIncrementCount = bAutoMajor ?
        m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
    if( nMaxMainIncrementCount > 1 )
        nMaxMainIncrementCount--;

    //choose major time interval:
    tools::Long nDayCount = aMaxDate - aMinDate;
    tools::Long nMainIncrementCount = 1;
    if( !bAutoMajor )
    {
        tools::Long nIntervalDayCount = rExplicitIncrement.MajorTimeInterval.Number;
        if( rExplicitIncrement.MajorTimeInterval.TimeUnit < rExplicitScale.TimeResolution )
            rExplicitIncrement.MajorTimeInterval.TimeUnit = rExplicitScale.TimeResolution;
        switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
        {
        case DAY:
            break;
        case MONTH:
            nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
            break;
        case YEAR:
            nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
            break;
        }
        nMainIncrementCount = nDayCount/nIntervalDayCount;
        if( nMainIncrementCount > nMaxMainIncrementCount )
            bAutoMajor = true;
    }
    if( bAutoMajor )
    {
        tools::Long nNumer = 1;
        tools::Long nIntervalDays =  nDayCount / nMaxMainIncrementCount;
        double nDaysPerInterval = 1.0;
        if( nIntervalDays>365 || rExplicitScale.TimeResolution==YEAR )
        {
            rExplicitIncrement.MajorTimeInterval.TimeUnit = YEAR;
            nDaysPerInterval = 365.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
        }
        else if( nIntervalDays>31 || rExplicitScale.TimeResolution==MONTH )
        {
            rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
            nDaysPerInterval = 31.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
        }
        else
        {
            rExplicitIncrement.MajorTimeInterval.TimeUnit = DAY;
            nDaysPerInterval = 1.0;
        }

        nNumer = static_cast<sal_Int32>( rtl::math::approxFloor( nIntervalDays/nDaysPerInterval ) );
        if(nNumer<=0)
            nNumer=1;
        if( rExplicitIncrement.MajorTimeInterval.TimeUnit == DAY )
        {
            if( nNumer>2 && nNumer<7 )
                nNumer=7;
            else if( nNumer>7 )
            {
                rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
                nDaysPerInterval = 31.0;
                nNumer = static_cast<sal_Int32>( rtl::math::approxFloor( nIntervalDays/nDaysPerInterval ) );
                if(nNumer<=0)
                    nNumer=1;
            }
        }
        rExplicitIncrement.MajorTimeInterval.Number = nNumer;
        nMainIncrementCount = static_cast<tools::Long>(nDayCount/(nNumer*nDaysPerInterval));
    }

    //choose minor time interval:
    if( !bAutoMinor )
    {
        if( rExplicitIncrement.MinorTimeInterval.TimeUnit > rExplicitIncrement.MajorTimeInterval.TimeUnit )
            rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
        tools::Long nIntervalDayCount = rExplicitIncrement.MinorTimeInterval.Number;
        switch( rExplicitIncrement.MinorTimeInterval.TimeUnit )
        {
        case DAY:
            break;
        case MONTH:
            nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
            break;
        case YEAR:
            nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
            break;
        }
        if( nDayCount/nIntervalDayCount > nMaxMainIncrementCount )
            bAutoMinor = true;
    }
    if( !bAutoMinor )
        return;

    rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
    rExplicitIncrement.MinorTimeInterval.Number = 1;
    if( nMainIncrementCount > 100 )
        rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
    else
    {
        if( rExplicitIncrement.MajorTimeInterval.Number >= 2 )
        {
            if( !(rExplicitIncrement.MajorTimeInterval.Number%2) )
                rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/2;
            else if( !(rExplicitIncrement.MajorTimeInterval.Number%3) )
                rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/3;
            else if( !(rExplicitIncrement.MajorTimeInterval.Number%5) )
                rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/5;
            else if( rExplicitIncrement.MajorTimeInterval.Number > 50 )
                rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
        }
        else
        {
            switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
            {
                case DAY:
                    break;
                case MONTH:
                    if( rExplicitScale.TimeResolution == DAY )
                        rExplicitIncrement.MinorTimeInterval.TimeUnit = DAY;
                    break;
                case YEAR:
                    if( rExplicitScale.TimeResolution <= MONTH )
                        rExplicitIncrement.MinorTimeInterval.TimeUnit = MONTH;
                    break;
            }
        }
    }

}

void ScaleAutomatism::calculateExplicitIncrementAndScaleForLinear(
        ExplicitScaleData& rExplicitScale,
        ExplicitIncrementData& rExplicitIncrement,
        bool bAutoMinimum, bool bAutoMaximum ) const
{
    // *** STEP 1: initialize the range data ***

    double fSourceMinimum = rExplicitScale.Minimum;
    double fSourceMaximum = rExplicitScale.Maximum;

    // set automatic PostEquidistant to true (maybe scaling dependent?)
    if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
        rExplicitIncrement.PostEquidistant = true;

    /*  If range is invalid (minimum greater than maximum), change one of the
        variable limits to validate the range. In this step, a zero-sized range
        is still allowed. */

    if( fSourceMinimum > fSourceMaximum )
    {
        // force changing the maximum, if both limits are fixed
        if( bAutoMaximum || !bAutoMinimum )
            fSourceMaximum = fSourceMinimum;
        else
            fSourceMinimum = fSourceMaximum;
    }

    /*  If maximum is zero or negative (and therefore minimum too), minimum and
        maximum will be negated and swapped to make the following algorithms
        easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
        [2,5], and the latter will be swapped back later. The range [0,0] is
        explicitly excluded from swapping (this would result in [-1,0] instead
        of the expected [0,1]). */

    bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
    if( bSwapAndNegateRange )
    {
        double fTempValue = fSourceMinimum;
        fSourceMinimum = -fSourceMaximum;
        fSourceMaximum = -fTempValue;
        std::swap( bAutoMinimum, bAutoMaximum );
    }

    // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***

    double fTempMinimum = fSourceMinimum;
    double fTempMaximum = fSourceMaximum;

    /*  If minimum is variable and greater than 0 (and therefore maximum too),
        means all values are positive (or all values are negative, and the
        range has been swapped above), then: */

    if( bAutoMinimum && (fTempMinimum > 0.0) )
    {
        /*  If minimum equals maximum, or if minimum is less than 5/6 of
            maximum, set minimum to 0. */

        if( (fTempMinimum == fTempMaximum) || (fTempMinimum / fTempMaximum < 5.0 / 6.0) )
        {
            if( m_bExpandWideValuesToZero )
                fTempMinimum = 0.0;
        }
        /*  Else (minimum is greater than or equal to 5/6 of maximum), add half
            of the visible range (expand minimum toward 0) to make the
            'shorter' data points visible. */

        else
        {
            if( m_bExpandNarrowValuesTowardZero )
                fTempMinimum -= (fTempMaximum - fTempMinimum) / 2.0;
        }
    }

    /*  If range is still zero-sized (e.g. when minimum is fixed), add some
        space to a variable limit. */

    if( fTempMinimum == fTempMaximum )
    {
        if( bAutoMaximum || !bAutoMinimum )
        {
            // change 0 to 1, otherwise double the value
            if( fTempMaximum == 0.0 )
                fTempMaximum = 1.0;
            else
                fTempMaximum *= 2.0;
        }
        else
        {
            // change 0 to -1, otherwise halve the value
            if( fTempMinimum == 0.0 )
                fTempMinimum = -1.0;
            else
                fTempMinimum /= 2.0;
        }
    }

    // *** STEP 3: calculate main interval size ***

    // base value (anchor position of the intervals)
    if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
    {
        if( !bAutoMinimum )
            rExplicitIncrement.BaseValue = fTempMinimum;
        else if( !bAutoMaximum )
            rExplicitIncrement.BaseValue = fTempMaximum;
        else
            rExplicitIncrement.BaseValue = 0.0;
    }

    // calculate automatic interval
    bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
    /*  Restrict number of allowed intervals with user-defined distance to
        MAXIMUM_MANUAL_INCREMENT_COUNT. */

    sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
        m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;

    double fDistanceMagnitude = 0.0;
    double fDistanceNormalized = 0.0;
    bool bHasNormalizedDistance = false;

    // repeat calculation until number of intervals are valid
    bool bNeedIteration = true;
    while( bNeedIteration )
    {
        if( bAutoDistance )
        {
            // first iteration: calculate interval size from axis limits
            if( !bHasNormalizedDistance )
            {
                // raw size of an interval
                double fDistance = (fTempMaximum - fTempMinimum) / nMaxMainIncrementCount;

                // if distance of is less than 1e-307, do not do anything
                if( fDistance <= 1.0e-307 )
                {
                    fDistanceNormalized = 1.0;
                    fDistanceMagnitude = 1.0e-307;
                }
                else if ( !std::isfinite(fDistance) )
                {
                    // fdo#43703: Handle values bigger than limits correctly
                    fDistanceNormalized = 1.0;
                    fDistanceMagnitude = std::numeric_limits<double>::max();
                }
                else
                {
                    // distance magnitude (a power of 10)
                    int nExponent = static_castint >( ::rtl::math::approxFloor( log10( fDistance ) ) );
                    fDistanceMagnitude = ::rtl::math::pow10Exp( 1.0, nExponent );

                    // stick normalized distance to a few predefined values
                    fDistanceNormalized = fDistance / fDistanceMagnitude;
                    if( fDistanceNormalized <= 1.0 )
                        fDistanceNormalized = 1.0;
                    else if( fDistanceNormalized <= 2.0 )
                        fDistanceNormalized = 2.0;
                    else if( fDistanceNormalized <= 5.0 )
                        fDistanceNormalized = 5.0;
                    else
                    {
                        fDistanceNormalized = 1.0;
                        fDistanceMagnitude *= 10;
                    }
                }
                // for next iteration: distance is normalized -> use else path to increase distance
                bHasNormalizedDistance = true;
            }
            // following iterations: increase distance, use only allowed values
            else
            {
                if( fDistanceNormalized == 1.0 )
                    fDistanceNormalized = 2.0;
                else if( fDistanceNormalized == 2.0 )
                    fDistanceNormalized = 5.0;
                else
                {
                    fDistanceNormalized = 1.0;
                    fDistanceMagnitude *= 10;
                }
            }

            // set the resulting distance
            rExplicitIncrement.Distance = fDistanceNormalized * fDistanceMagnitude;
        }

        // *** STEP 4: additional space above or below the data points ***

        double fAxisMinimum = fTempMinimum;
        double fAxisMaximum = fTempMaximum;

        // round to entire multiples of the distance and add additional space
        if( bAutoMinimum )
        {
            // round to entire multiples of the distance, based on the base value
            if( m_bExpandBorderToIncrementRhythm )
                fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );
            // additional space, if source minimum is to near at axis minimum
            if( m_bExpandIfValuesCloseToBorder )
                if( (fAxisMinimum != 0.0) && ((fAxisMaximum - fSourceMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
                    fAxisMinimum -= rExplicitIncrement.Distance;
        }
        if( bAutoMaximum )
        {
            // round to entire multiples of the distance, based on the base value
            if( m_bExpandBorderToIncrementRhythm )
                fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
            // additional space, if source maximum is to near at axis maximum
            if( m_bExpandIfValuesCloseToBorder )
                if( (fAxisMaximum != 0.0) && ((fSourceMaximum - fAxisMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
                    fAxisMaximum += rExplicitIncrement.Distance;
        }

        // set the resulting limits (swap back to negative range if needed)
        if( bSwapAndNegateRange )
        {
            rExplicitScale.Minimum = -fAxisMaximum;
            rExplicitScale.Maximum = -fAxisMinimum;
        }
        else
        {
            rExplicitScale.Minimum = fAxisMinimum;
            rExplicitScale.Maximum = fAxisMaximum;
        }

        /*  If the number of intervals is too high (e.g. due to invalid fixed
            distance or due to added space above or below data points),
            calculate again with increased distance. */

        double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
        bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
        // if manual distance is invalid, trigger automatic calculation
        if( bNeedIteration )
            bAutoDistance = true;
    }

    //fill explicit sub increment
    sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
    for( sal_Int32 nN=0; nN<nSubCount; nN++ )
    {
        ExplicitSubIncrement aExplicitSubIncrement;
        const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
        if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
        {
            //scaling dependent
            //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
            aExplicitSubIncrement.IntervalCount = 2;
        }
        lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
        if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
        {
            //scaling dependent
            aExplicitSubIncrement.PostEquidistant = false;
        }
        rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
    }
}

//namespace chart

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=89 H=98 G=93

¤ Dauer der Verarbeitung: 0.10 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.