/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: */ |