Coverage Report

Created: 2026-09-28 10:59

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