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