fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / chart2 / source / view / axes / ScaleAutomatism.cxx
blobf58bde44598164bdc60dcf8aaf29003dc1cd4522
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "ScaleAutomatism.hxx"
21 #include "macros.hxx"
22 #include "Tickmarks_Equidistant.hxx"
23 #include "DateHelper.hxx"
24 #include "DateScaling.hxx"
25 #include "AxisHelper.hxx"
26 #include <com/sun/star/chart/TimeUnit.hpp>
28 #include <rtl/math.hxx>
29 #include <limits>
31 namespace chart
33 using namespace ::com::sun::star;
34 using namespace ::com::sun::star::chart2;
35 using ::com::sun::star::chart::TimeUnit::DAY;
36 using ::com::sun::star::chart::TimeUnit::MONTH;
37 using ::com::sun::star::chart::TimeUnit::YEAR;
39 const sal_Int32 MAXIMUM_MANUAL_INCREMENT_COUNT = 500;
40 const sal_Int32 MAXIMUM_SUB_INCREMENT_COUNT = 100;
42 sal_Int32 lcl_getMaximumAutoIncrementCount( sal_Int32 nAxisType )
44 sal_Int32 nMaximumAutoIncrementCount = 10;
45 if( nAxisType==AxisType::DATE )
46 nMaximumAutoIncrementCount = MAXIMUM_MANUAL_INCREMENT_COUNT;
47 return nMaximumAutoIncrementCount;
50 namespace
53 void lcl_ensureMaximumSubIncrementCount( sal_Int32& rnSubIntervalCount )
55 if( rnSubIntervalCount > MAXIMUM_SUB_INCREMENT_COUNT )
56 rnSubIntervalCount = MAXIMUM_SUB_INCREMENT_COUNT;
59 }//end anonymous namespace
61 ExplicitScaleData::ExplicitScaleData()
62 : Minimum(0.0)
63 , Maximum(10.0)
64 , Origin(0.0)
65 , Orientation(::com::sun::star::chart2::AxisOrientation_MATHEMATICAL)
66 , Scaling()
67 , AxisType(::com::sun::star::chart2::AxisType::REALNUMBER)
68 , ShiftedCategoryPosition(false)
69 , TimeResolution(::com::sun::star::chart::TimeUnit::DAY)
70 , NullDate(30,12,1899)
74 ExplicitSubIncrement::ExplicitSubIncrement()
75 : IntervalCount(2)
76 , PostEquidistant(true)
80 ExplicitIncrementData::ExplicitIncrementData()
81 : MajorTimeInterval(1,::com::sun::star::chart::TimeUnit::DAY)
82 , MinorTimeInterval(1,::com::sun::star::chart::TimeUnit::DAY)
83 , Distance(1.0)
84 , PostEquidistant(true)
85 , BaseValue(0.0)
86 , SubIncrements()
90 ScaleAutomatism::ScaleAutomatism( const ScaleData& rSourceScale, const Date& rNullDate )
91 : m_aSourceScale( rSourceScale )
92 , m_fValueMinimum( 0.0 )
93 , m_fValueMaximum( 0.0 )
94 , m_nMaximumAutoMainIncrementCount( lcl_getMaximumAutoIncrementCount( rSourceScale.AxisType ) )
95 , m_bExpandBorderToIncrementRhythm( false )
96 , m_bExpandIfValuesCloseToBorder( false )
97 , m_bExpandWideValuesToZero( false )
98 , m_bExpandNarrowValuesTowardZero( false )
99 , m_nTimeResolution(::com::sun::star::chart::TimeUnit::DAY)
100 , m_aNullDate(rNullDate)
102 ::rtl::math::setNan( &m_fValueMinimum );
103 ::rtl::math::setNan( &m_fValueMaximum );
105 double fExplicitOrigin = 0.0;
106 if( m_aSourceScale.Origin >>= fExplicitOrigin )
107 expandValueRange( fExplicitOrigin, fExplicitOrigin);
109 ScaleAutomatism::~ScaleAutomatism()
113 void ScaleAutomatism::expandValueRange( double fMinimum, double fMaximum )
115 if( (fMinimum < m_fValueMinimum) || ::rtl::math::isNan( m_fValueMinimum ) )
116 m_fValueMinimum = fMinimum;
117 if( (fMaximum > m_fValueMaximum) || ::rtl::math::isNan( m_fValueMaximum ) )
118 m_fValueMaximum = fMaximum;
121 void ScaleAutomatism::setAutoScalingOptions(
122 bool bExpandBorderToIncrementRhythm,
123 bool bExpandIfValuesCloseToBorder,
124 bool bExpandWideValuesToZero,
125 bool bExpandNarrowValuesTowardZero )
127 // if called multiple times, enable an option, if it is set in at least one call
128 m_bExpandBorderToIncrementRhythm |= bExpandBorderToIncrementRhythm;
129 m_bExpandIfValuesCloseToBorder |= bExpandIfValuesCloseToBorder;
130 m_bExpandWideValuesToZero |= bExpandWideValuesToZero;
131 m_bExpandNarrowValuesTowardZero |= bExpandNarrowValuesTowardZero;
133 if( m_aSourceScale.AxisType==AxisType::PERCENT )
134 m_bExpandIfValuesCloseToBorder = false;
137 void ScaleAutomatism::setMaximumAutoMainIncrementCount( sal_Int32 nMaximumAutoMainIncrementCount )
139 if( nMaximumAutoMainIncrementCount < 2 )
140 m_nMaximumAutoMainIncrementCount = 2; //#i82006
141 else if( nMaximumAutoMainIncrementCount > lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType ) )
142 m_nMaximumAutoMainIncrementCount = lcl_getMaximumAutoIncrementCount( m_aSourceScale.AxisType );
143 else
144 m_nMaximumAutoMainIncrementCount = nMaximumAutoMainIncrementCount;
147 void ScaleAutomatism::setAutomaticTimeResolution( sal_Int32 nTimeResolution )
149 m_nTimeResolution = nTimeResolution;
152 void ScaleAutomatism::calculateExplicitScaleAndIncrement(
153 ExplicitScaleData& rExplicitScale, ExplicitIncrementData& rExplicitIncrement ) const
155 // fill explicit scale
156 rExplicitScale.Orientation = m_aSourceScale.Orientation;
157 rExplicitScale.Scaling = m_aSourceScale.Scaling;
158 rExplicitScale.AxisType = m_aSourceScale.AxisType;
159 rExplicitScale.NullDate = m_aNullDate;
161 bool bAutoMinimum = !(m_aSourceScale.Minimum >>= rExplicitScale.Minimum);
162 bool bAutoMaximum = !(m_aSourceScale.Maximum >>= rExplicitScale.Maximum);
163 bool bAutoOrigin = !(m_aSourceScale.Origin >>= rExplicitScale.Origin);
165 // automatic scale minimum
166 if( bAutoMinimum )
168 if( m_aSourceScale.AxisType==AxisType::PERCENT )
169 rExplicitScale.Minimum = 0.0;
170 else if( ::rtl::math::isNan( m_fValueMinimum ) )
172 if( m_aSourceScale.AxisType==AxisType::DATE )
173 rExplicitScale.Minimum = 36526.0; //1.1.2000
174 else
175 rExplicitScale.Minimum = 0.0; //@todo get Minimum from scaling or from plotter????
177 else
178 rExplicitScale.Minimum = m_fValueMinimum;
181 // automatic scale maximum
182 if( bAutoMaximum )
184 if( m_aSourceScale.AxisType==AxisType::PERCENT )
185 rExplicitScale.Maximum = 1.0;
186 else if( ::rtl::math::isNan( m_fValueMaximum ) )
188 if( m_aSourceScale.AxisType==AxisType::DATE )
189 rExplicitScale.Maximum = 40179.0; //1.1.2010
190 else
191 rExplicitScale.Maximum = 10.0; //@todo get Maximum from scaling or from plotter????
193 else
194 rExplicitScale.Maximum = m_fValueMaximum;
197 //fill explicit increment
199 rExplicitScale.ShiftedCategoryPosition = m_aSourceScale.ShiftedCategoryPosition;
200 bool bIsLogarithm = false;
202 //minimum and maximum of the ExplicitScaleData may be changed if allowed
203 if( m_aSourceScale.AxisType==AxisType::DATE )
204 calculateExplicitIncrementAndScaleForDateTimeAxis( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
205 else if( m_aSourceScale.AxisType==AxisType::CATEGORY || m_aSourceScale.AxisType==AxisType::SERIES )
206 calculateExplicitIncrementAndScaleForCategory( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
207 else
209 bIsLogarithm = AxisHelper::isLogarithmic( rExplicitScale.Scaling );
210 if( bIsLogarithm )
211 calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
212 else
213 calculateExplicitIncrementAndScaleForLinear( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum );
216 // automatic origin
217 if( bAutoOrigin )
219 // #i71415# automatic origin for logarithmic axis
220 double fDefaulOrigin = bIsLogarithm ? 1.0 : 0.0;
222 if( fDefaulOrigin < rExplicitScale.Minimum )
223 fDefaulOrigin = rExplicitScale.Minimum;
224 else if( fDefaulOrigin > rExplicitScale.Maximum )
225 fDefaulOrigin = rExplicitScale.Maximum;
227 rExplicitScale.Origin = fDefaulOrigin;
231 void ScaleAutomatism::calculateExplicitIncrementAndScaleForCategory(
232 ExplicitScaleData& rExplicitScale,
233 ExplicitIncrementData& rExplicitIncrement,
234 bool bAutoMinimum, bool bAutoMaximum ) const
236 // no scaling for categories
237 rExplicitScale.Scaling.clear();
239 if( rExplicitScale.ShiftedCategoryPosition )
240 rExplicitScale.Maximum += 1.0;
242 // ensure that at least one category is visible
243 if( rExplicitScale.Maximum <= rExplicitScale.Minimum )
244 rExplicitScale.Maximum = rExplicitScale.Minimum + 1.0;
246 // default increment settings
247 rExplicitIncrement.PostEquidistant = true; // does not matter anyhow
248 rExplicitIncrement.Distance = 1.0; // category axis always have a main increment of 1
249 rExplicitIncrement.BaseValue = 0.0; // category axis always have a base of 0
251 // automatic minimum and maximum
252 if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
253 rExplicitScale.Minimum = EquidistantTickFactory::getMinimumAtIncrement( rExplicitScale.Minimum, rExplicitIncrement );
254 if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
255 rExplicitScale.Maximum = EquidistantTickFactory::getMaximumAtIncrement( rExplicitScale.Maximum, rExplicitIncrement );
257 //prevent performace killover
258 double fDistanceCount = ::rtl::math::approxFloor( (rExplicitScale.Maximum-rExplicitScale.Minimum) / rExplicitIncrement.Distance );
259 if( static_cast< sal_Int32 >( fDistanceCount ) > MAXIMUM_MANUAL_INCREMENT_COUNT )
261 double fMinimumFloor = ::rtl::math::approxFloor( rExplicitScale.Minimum );
262 double fMaximumCeil = ::rtl::math::approxCeil( rExplicitScale.Maximum );
263 rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / MAXIMUM_MANUAL_INCREMENT_COUNT );
266 //fill explicit sub increment
267 sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
268 for( sal_Int32 nN=0; nN<nSubCount; nN++ )
270 ExplicitSubIncrement aExplicitSubIncrement;
271 const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
272 if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
274 //scaling dependent
275 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
276 aExplicitSubIncrement.IntervalCount = 2;
278 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
279 if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
281 //scaling dependent
282 aExplicitSubIncrement.PostEquidistant = false;
284 rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
288 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLogarithmic(
289 ExplicitScaleData& rExplicitScale,
290 ExplicitIncrementData& rExplicitIncrement,
291 bool bAutoMinimum, bool bAutoMaximum ) const
293 // *** STEP 1: initialize the range data ***
295 const double fInputMinimum = rExplicitScale.Minimum;
296 const double fInputMaximum = rExplicitScale.Maximum;
298 double fSourceMinimum = rExplicitScale.Minimum;
299 double fSourceMaximum = rExplicitScale.Maximum;
301 // set automatic PostEquidistant to true (maybe scaling dependent?)
302 // Note: scaling with PostEquidistant==false is untested and needs review
303 if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
304 rExplicitIncrement.PostEquidistant = true;
306 /* All following scaling code will operate on the logarithms of the source
307 values. In the last step, the original values will be restored. */
308 uno::Reference< XScaling > xScaling = rExplicitScale.Scaling;
309 if( !xScaling.is() )
310 xScaling.set( AxisHelper::createLogarithmicScaling() );
311 uno::Reference< XScaling > xInverseScaling = xScaling->getInverseScaling();
313 fSourceMinimum = xScaling->doScaling( fSourceMinimum );
314 if( !::rtl::math::isFinite( fSourceMinimum ) )
315 fSourceMinimum = 0.0;
316 else if( ::rtl::math::approxEqual( fSourceMinimum, ::rtl::math::approxFloor( fSourceMinimum ) ) )
317 fSourceMinimum = ::rtl::math::approxFloor( fSourceMinimum );
319 fSourceMaximum = xScaling->doScaling( fSourceMaximum );
320 if( !::rtl::math::isFinite( fSourceMaximum ) )
321 fSourceMaximum = 0.0;
322 else if( ::rtl::math::approxEqual( fSourceMaximum, ::rtl::math::approxFloor( fSourceMaximum ) ) )
323 fSourceMaximum = ::rtl::math::approxFloor( fSourceMaximum );
325 /* If range is invalid (minimum greater than maximum), change one of the
326 variable limits to validate the range. In this step, a zero-sized range
327 is still allowed. */
328 if( fSourceMinimum > fSourceMaximum )
330 // force changing the maximum, if both limits are fixed
331 if( bAutoMaximum || !bAutoMinimum )
332 fSourceMaximum = fSourceMinimum;
333 else
334 fSourceMinimum = fSourceMaximum;
337 /* If maximum is less than 0 (and therefore minimum too), minimum and
338 maximum will be negated and swapped to make the following algorithms
339 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
340 [2,5], and the latter will be swapped back later. The range [0,0] is
341 explicitly excluded from swapping (this would result in [-1,0] instead
342 of the expected [0,1]). */
343 bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
344 if( bSwapAndNegateRange )
346 double fTempValue = fSourceMinimum;
347 fSourceMinimum = -fSourceMaximum;
348 fSourceMaximum = -fTempValue;
349 ::std::swap( bAutoMinimum, bAutoMaximum );
352 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
354 double fTempMinimum = fSourceMinimum;
355 double fTempMaximum = fSourceMaximum;
357 /* If minimum is variable and greater than 0 (and therefore maximum too),
358 means all original values are greater than 1 (or all values are less
359 than 1, and the range has been swapped above), then: */
360 if( bAutoMinimum && (fTempMinimum > 0.0) )
362 /* If minimum is less than 5 (i.e. original source values less than
363 B^5, B being the base of the scaling), or if minimum and maximum
364 are in different increment intervals (means, if minimum and maximum
365 are not both in the range [B^n,B^(n+1)] for a whole number n), set
366 minimum to 0, which results in B^0=1 on the axis. */
367 double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
368 double fMaximumFloor = ::rtl::math::approxFloor( fTempMaximum );
369 // handle the exact value B^(n+1) to be in the range [B^n,B^(n+1)]
370 if( ::rtl::math::approxEqual( fTempMaximum, fMaximumFloor ) )
371 fMaximumFloor -= 1.0;
373 if( (fMinimumFloor < 5.0) || (fMinimumFloor < fMaximumFloor) )
375 if( m_bExpandWideValuesToZero )
376 fTempMinimum = 0.0;
378 /* Else (minimum and maximum are in one increment interval), expand
379 minimum toward 0 to make the 'shorter' data points visible. */
380 else
382 if( m_bExpandNarrowValuesTowardZero )
383 fTempMinimum -= 1.0;
387 /* If range is still zero-sized (e.g. when minimum is fixed), set minimum
388 to 0, which makes the axis start/stop at the value 1. */
389 if( fTempMinimum == fTempMaximum )
391 if( bAutoMinimum && (fTempMaximum > 0.0) )
392 fTempMinimum = 0.0;
393 else
394 fTempMaximum += 1.0; // always add one interval, even if maximum is fixed
397 // *** STEP 3: calculate main interval size ***
399 // base value (anchor position of the intervals), already scaled
400 if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
402 //scaling dependent
403 //@maybe todo is this default also plotter dependent ??
404 if( !bAutoMinimum )
405 rExplicitIncrement.BaseValue = fTempMinimum;
406 else if( !bAutoMaximum )
407 rExplicitIncrement.BaseValue = fTempMaximum;
408 else
409 rExplicitIncrement.BaseValue = 0.0;
412 // calculate automatic interval
413 bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
414 if( bAutoDistance )
415 rExplicitIncrement.Distance = 0.0;
417 /* Restrict number of allowed intervals with user-defined distance to
418 MAXIMUM_MANUAL_INCREMENT_COUNT. */
419 sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
420 m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
422 // repeat calculation until number of intervals are valid
423 bool bNeedIteration = true;
424 bool bHasCalculatedDistance = false;
425 while( bNeedIteration )
427 if( bAutoDistance )
429 // first iteration: calculate interval size from axis limits
430 if( !bHasCalculatedDistance )
432 double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum );
433 double fMaximumCeil = ::rtl::math::approxCeil( fTempMaximum );
434 rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / nMaxMainIncrementCount );
436 else
438 // following iterations: increase distance
439 rExplicitIncrement.Distance += 1.0;
442 // for next iteration: distance calculated -> use else path to increase
443 bHasCalculatedDistance = true;
446 // *** STEP 4: additional space above or below the data points ***
448 double fAxisMinimum = fTempMinimum;
449 double fAxisMaximum = fTempMaximum;
451 // round to entire multiples of the distance and add additional space
452 if( bAutoMinimum && m_bExpandBorderToIncrementRhythm )
454 fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );
456 //ensure valid values after scaling #i100995#
457 if( !bAutoDistance )
459 double fCheck = xInverseScaling->doScaling( fAxisMinimum );
460 if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 )
462 bAutoDistance = true;
463 bHasCalculatedDistance = false;
464 continue;
468 if( bAutoMaximum && m_bExpandBorderToIncrementRhythm )
470 fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
472 //ensure valid values after scaling #i100995#
473 if( !bAutoDistance )
475 double fCheck = xInverseScaling->doScaling( fAxisMaximum );
476 if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 )
478 bAutoDistance = true;
479 bHasCalculatedDistance = false;
480 continue;
485 // set the resulting limits (swap back to negative range if needed)
486 if( bSwapAndNegateRange )
488 rExplicitScale.Minimum = -fAxisMaximum;
489 rExplicitScale.Maximum = -fAxisMinimum;
491 else
493 rExplicitScale.Minimum = fAxisMinimum;
494 rExplicitScale.Maximum = fAxisMaximum;
497 /* If the number of intervals is too high (e.g. due to invalid fixed
498 distance or due to added space above or below data points),
499 calculate again with increased distance. */
500 double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
501 bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
502 // if manual distance is invalid, trigger automatic calculation
503 if( bNeedIteration )
504 bAutoDistance = true;
506 // convert limits back to logarithmic scale
507 rExplicitScale.Minimum = xInverseScaling->doScaling( rExplicitScale.Minimum );
508 rExplicitScale.Maximum = xInverseScaling->doScaling( rExplicitScale.Maximum );
510 //ensure valid values after scaling #i100995#
511 if( !::rtl::math::isFinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0)
513 rExplicitScale.Minimum = fInputMinimum;
514 if( !::rtl::math::isFinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0 )
515 rExplicitScale.Minimum = 1.0;
517 if( !::rtl::math::isFinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
519 rExplicitScale.Maximum= fInputMaximum;
520 if( !::rtl::math::isFinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 )
521 rExplicitScale.Maximum = 10.0;
523 if( rExplicitScale.Maximum < rExplicitScale.Minimum )
524 ::std::swap( rExplicitScale.Maximum, rExplicitScale.Minimum );
527 //fill explicit sub increment
528 sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
529 for( sal_Int32 nN=0; nN<nSubCount; nN++ )
531 ExplicitSubIncrement aExplicitSubIncrement;
532 const SubIncrement& rSubIncrement = m_aSourceScale.IncrementData.SubIncrements[nN];
533 if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
535 //scaling dependent
536 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
537 aExplicitSubIncrement.IntervalCount = 9;
539 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
540 if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
542 //scaling dependent
543 aExplicitSubIncrement.PostEquidistant = false;
545 rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
549 void ScaleAutomatism::calculateExplicitIncrementAndScaleForDateTimeAxis(
550 ExplicitScaleData& rExplicitScale,
551 ExplicitIncrementData& rExplicitIncrement,
552 bool bAutoMinimum, bool bAutoMaximum ) const
554 Date aMinDate(m_aNullDate); aMinDate += static_cast<long>(::rtl::math::approxFloor(rExplicitScale.Minimum));
555 Date aMaxDate(m_aNullDate); aMaxDate += static_cast<long>(::rtl::math::approxFloor(rExplicitScale.Maximum));
556 rExplicitIncrement.PostEquidistant = false;
558 if( aMinDate > aMaxDate )
560 std::swap(aMinDate,aMaxDate);
563 if( !(m_aSourceScale.TimeIncrement.TimeResolution >>= rExplicitScale.TimeResolution) )
564 rExplicitScale.TimeResolution = m_nTimeResolution;
566 rExplicitScale.Scaling = new DateScaling(m_aNullDate,rExplicitScale.TimeResolution,false);
568 // choose min and max suitable to time resolution
569 switch( rExplicitScale.TimeResolution )
571 case DAY:
572 if( rExplicitScale.ShiftedCategoryPosition )
573 ++aMaxDate; //for explicit scales we need one interval more (maximum excluded)
574 break;
575 case MONTH:
576 aMinDate.SetDay(1);
577 aMaxDate.SetDay(1);
578 if( rExplicitScale.ShiftedCategoryPosition )
579 aMaxDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
580 if( DateHelper::IsLessThanOneMonthAway( aMinDate, aMaxDate ) )
582 if( bAutoMaximum || !bAutoMinimum )
583 aMaxDate = DateHelper::GetDateSomeMonthsAway(aMinDate,1);
584 else
585 aMinDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,-1);
587 break;
588 case YEAR:
589 aMinDate.SetDay(1);
590 aMinDate.SetMonth(1);
591 aMaxDate.SetDay(1);
592 aMaxDate.SetMonth(1);
593 if( rExplicitScale.ShiftedCategoryPosition )
594 aMaxDate = DateHelper::GetDateSomeYearsAway(aMaxDate,1);//for explicit scales we need one interval more (maximum excluded)
595 if( DateHelper::IsLessThanOneYearAway( aMinDate, aMaxDate ) )
597 if( bAutoMaximum || !bAutoMinimum )
598 aMaxDate = DateHelper::GetDateSomeYearsAway(aMinDate,1);
599 else
600 aMinDate = DateHelper::GetDateSomeYearsAway(aMaxDate,-1);
602 break;
605 // set the resulting limits (swap back to negative range if needed)
606 rExplicitScale.Minimum = aMinDate - m_aNullDate;
607 rExplicitScale.Maximum = aMaxDate - m_aNullDate;
609 bool bAutoMajor = !(m_aSourceScale.TimeIncrement.MajorTimeInterval >>= rExplicitIncrement.MajorTimeInterval);
610 bool bAutoMinor = !(m_aSourceScale.TimeIncrement.MinorTimeInterval >>= rExplicitIncrement.MinorTimeInterval);
612 sal_Int32 nMaxMainIncrementCount = bAutoMajor ?
613 m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
614 if( nMaxMainIncrementCount > 1 )
615 nMaxMainIncrementCount--;
617 //choose major time interval:
618 long nDayCount = (aMaxDate-aMinDate);
619 long nMainIncrementCount = 1;
620 if( !bAutoMajor )
622 long nIntervalDayCount = rExplicitIncrement.MajorTimeInterval.Number;
623 if( rExplicitIncrement.MajorTimeInterval.TimeUnit < rExplicitScale.TimeResolution )
624 rExplicitIncrement.MajorTimeInterval.TimeUnit = rExplicitScale.TimeResolution;
625 switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
627 case DAY:
628 break;
629 case MONTH:
630 nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
631 break;
632 case YEAR:
633 nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
634 break;
636 nMainIncrementCount = nDayCount/nIntervalDayCount;
637 if( nMainIncrementCount > nMaxMainIncrementCount )
638 bAutoMajor = true;
640 if( bAutoMajor )
642 long nNumer = 1;
643 long nIntervalDays = nDayCount / nMaxMainIncrementCount;
644 double nDaysPerInterval = 1.0;
645 if( nIntervalDays>365 || YEAR==rExplicitScale.TimeResolution )
647 rExplicitIncrement.MajorTimeInterval.TimeUnit = YEAR;
648 nDaysPerInterval = 365.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
650 else if( nIntervalDays>31 || MONTH==rExplicitScale.TimeResolution )
652 rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
653 nDaysPerInterval = 31.0;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
655 else
657 rExplicitIncrement.MajorTimeInterval.TimeUnit = DAY;
658 nDaysPerInterval = 1.0;
661 nNumer = static_cast<sal_Int32>( rtl::math::approxCeil( nIntervalDays/nDaysPerInterval ) );
662 if(nNumer<=0)
663 nNumer=1;
664 if( rExplicitIncrement.MajorTimeInterval.TimeUnit == DAY )
666 if( nNumer>2 && nNumer<7 )
667 nNumer=7;
668 else if( nNumer>7 )
670 rExplicitIncrement.MajorTimeInterval.TimeUnit = MONTH;
671 nDaysPerInterval = 31.0;
672 nNumer = static_cast<sal_Int32>( rtl::math::approxCeil( nIntervalDays/nDaysPerInterval ) );
673 if(nNumer<=0)
674 nNumer=1;
677 rExplicitIncrement.MajorTimeInterval.Number = nNumer;
678 nMainIncrementCount = static_cast<long>(nDayCount/(nNumer*nDaysPerInterval));
681 //choose minor time interval:
682 if( !bAutoMinor )
684 if( rExplicitIncrement.MinorTimeInterval.TimeUnit > rExplicitIncrement.MajorTimeInterval.TimeUnit )
685 rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
686 long nIntervalDayCount = rExplicitIncrement.MinorTimeInterval.Number;
687 switch( rExplicitIncrement.MinorTimeInterval.TimeUnit )
689 case DAY:
690 break;
691 case MONTH:
692 nIntervalDayCount*=31;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
693 break;
694 case YEAR:
695 nIntervalDayCount*=365;//todo: maybe different for other calendars... get localized calendar according to set number format at axis ...
696 break;
698 if( nDayCount/nIntervalDayCount > nMaxMainIncrementCount )
699 bAutoMinor = true;
701 if( bAutoMinor )
703 rExplicitIncrement.MinorTimeInterval.TimeUnit = rExplicitIncrement.MajorTimeInterval.TimeUnit;
704 rExplicitIncrement.MinorTimeInterval.Number = 1;
705 if( nMainIncrementCount > 100 )
706 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
707 else
709 if( rExplicitIncrement.MajorTimeInterval.Number >= 2 )
711 if( !(rExplicitIncrement.MajorTimeInterval.Number%2) )
712 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/2;
713 else if( !(rExplicitIncrement.MajorTimeInterval.Number%3) )
714 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/3;
715 else if( !(rExplicitIncrement.MajorTimeInterval.Number%5) )
716 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number/5;
717 else if( rExplicitIncrement.MajorTimeInterval.Number > 50 )
718 rExplicitIncrement.MinorTimeInterval.Number = rExplicitIncrement.MajorTimeInterval.Number;
720 else
722 switch( rExplicitIncrement.MajorTimeInterval.TimeUnit )
724 case DAY:
725 break;
726 case MONTH:
727 if( rExplicitScale.TimeResolution == DAY )
728 rExplicitIncrement.MinorTimeInterval.TimeUnit = DAY;
729 break;
730 case YEAR:
731 if( rExplicitScale.TimeResolution <= MONTH )
732 rExplicitIncrement.MinorTimeInterval.TimeUnit = MONTH;
733 break;
741 void ScaleAutomatism::calculateExplicitIncrementAndScaleForLinear(
742 ExplicitScaleData& rExplicitScale,
743 ExplicitIncrementData& rExplicitIncrement,
744 bool bAutoMinimum, bool bAutoMaximum ) const
746 // *** STEP 1: initialize the range data ***
748 double fSourceMinimum = rExplicitScale.Minimum;
749 double fSourceMaximum = rExplicitScale.Maximum;
751 // set automatic PostEquidistant to true (maybe scaling dependent?)
752 if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) )
753 rExplicitIncrement.PostEquidistant = true;
755 /* If range is invalid (minimum greater than maximum), change one of the
756 variable limits to validate the range. In this step, a zero-sized range
757 is still allowed. */
758 if( fSourceMinimum > fSourceMaximum )
760 // force changing the maximum, if both limits are fixed
761 if( bAutoMaximum || !bAutoMinimum )
762 fSourceMaximum = fSourceMinimum;
763 else
764 fSourceMinimum = fSourceMaximum;
767 /* If maximum is zero or negative (and therefore minimum too), minimum and
768 maximum will be negated and swapped to make the following algorithms
769 easier. Example: Both ranges [2,5] and [-5,-2] will be processed as
770 [2,5], and the latter will be swapped back later. The range [0,0] is
771 explicitly excluded from swapping (this would result in [-1,0] instead
772 of the expected [0,1]). */
773 bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0);
774 if( bSwapAndNegateRange )
776 double fTempValue = fSourceMinimum;
777 fSourceMinimum = -fSourceMaximum;
778 fSourceMaximum = -fTempValue;
779 ::std::swap( bAutoMinimum, bAutoMaximum );
782 // *** STEP 2: find temporary (unrounded) axis minimum and maximum ***
784 double fTempMinimum = fSourceMinimum;
785 double fTempMaximum = fSourceMaximum;
787 /* If minimum is variable and greater than 0 (and therefore maximum too),
788 means all values are positive (or all values are negative, and the
789 range has been swapped above), then: */
790 if( bAutoMinimum && (fTempMinimum > 0.0) )
792 /* If minimum equals maximum, or if minimum is less than 5/6 of
793 maximum, set minimum to 0. */
794 if( (fTempMinimum == fTempMaximum) || (fTempMinimum / fTempMaximum < 5.0 / 6.0) )
796 if( m_bExpandWideValuesToZero )
797 fTempMinimum = 0.0;
799 /* Else (minimum is greater than or equal to 5/6 of maximum), add half
800 of the visible range (expand minimum toward 0) to make the
801 'shorter' data points visible. */
802 else
804 if( m_bExpandNarrowValuesTowardZero )
805 fTempMinimum -= (fTempMaximum - fTempMinimum) / 2.0;
809 /* If range is still zero-sized (e.g. when minimum is fixed), add some
810 space to a variable limit. */
811 if( fTempMinimum == fTempMaximum )
813 if( bAutoMaximum || !bAutoMinimum )
815 // change 0 to 1, otherwise double the value
816 if( fTempMaximum == 0.0 )
817 fTempMaximum = 1.0;
818 else
819 fTempMaximum *= 2.0;
821 else
823 // change 0 to -1, otherwise halve the value
824 if( fTempMinimum == 0.0 )
825 fTempMinimum = -1.0;
826 else
827 fTempMinimum /= 2.0;
831 // *** STEP 3: calculate main interval size ***
833 // base value (anchor position of the intervals)
834 if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) )
836 if( !bAutoMinimum )
837 rExplicitIncrement.BaseValue = fTempMinimum;
838 else if( !bAutoMaximum )
839 rExplicitIncrement.BaseValue = fTempMaximum;
840 else
841 rExplicitIncrement.BaseValue = 0.0;
844 // calculate automatic interval
845 bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance);
846 /* Restrict number of allowed intervals with user-defined distance to
847 MAXIMUM_MANUAL_INCREMENT_COUNT. */
848 sal_Int32 nMaxMainIncrementCount = bAutoDistance ?
849 m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT;
851 double fDistanceMagnitude = 0.0;
852 double fDistanceNormalized = 0.0;
853 bool bHasNormalizedDistance = false;
855 // repeat calculation until number of intervals are valid
856 bool bNeedIteration = true;
857 while( bNeedIteration )
859 if( bAutoDistance )
861 // first iteration: calculate interval size from axis limits
862 if( !bHasNormalizedDistance )
864 // raw size of an interval
865 double fDistance = (fTempMaximum - fTempMinimum) / nMaxMainIncrementCount;
867 // if distance of is less than 1e-307, do not do anything
868 if( fDistance <= 1.0e-307 )
870 fDistanceNormalized = 1.0;
871 fDistanceMagnitude = 1.0e-307;
873 else if ( !rtl::math::isFinite(fDistance) )
875 // fdo#43703: Handle values bigger than limits correctly
876 fDistanceNormalized = 1.0;
877 fDistanceMagnitude = std::numeric_limits<double>::max();
879 else
881 // distance magnitude (a power of 10)
882 int nExponent = static_cast< int >( ::rtl::math::approxFloor( log10( fDistance ) ) );
883 fDistanceMagnitude = ::rtl::math::pow10Exp( 1.0, nExponent );
885 // stick normalized distance to a few predefined values
886 fDistanceNormalized = fDistance / fDistanceMagnitude;
887 if( fDistanceNormalized <= 1.0 )
888 fDistanceNormalized = 1.0;
889 else if( fDistanceNormalized <= 2.0 )
890 fDistanceNormalized = 2.0;
891 else if( fDistanceNormalized <= 5.0 )
892 fDistanceNormalized = 5.0;
893 else
895 fDistanceNormalized = 1.0;
896 fDistanceMagnitude *= 10;
899 // for next iteration: distance is normalized -> use else path to increase distance
900 bHasNormalizedDistance = true;
902 // following iterations: increase distance, use only allowed values
903 else
905 if( fDistanceNormalized == 1.0 )
906 fDistanceNormalized = 2.0;
907 else if( fDistanceNormalized == 2.0 )
908 fDistanceNormalized = 5.0;
909 else
911 fDistanceNormalized = 1.0;
912 fDistanceMagnitude *= 10;
916 // set the resulting distance
917 rExplicitIncrement.Distance = fDistanceNormalized * fDistanceMagnitude;
920 // *** STEP 4: additional space above or below the data points ***
922 double fAxisMinimum = fTempMinimum;
923 double fAxisMaximum = fTempMaximum;
925 // round to entire multiples of the distance and add additional space
926 if( bAutoMinimum )
928 // round to entire multiples of the distance, based on the base value
929 if( m_bExpandBorderToIncrementRhythm )
930 fAxisMinimum = EquidistantTickFactory::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement );
931 // additional space, if source minimum is to near at axis minimum
932 if( m_bExpandIfValuesCloseToBorder )
933 if( (fAxisMinimum != 0.0) && ((fAxisMaximum - fSourceMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
934 fAxisMinimum -= rExplicitIncrement.Distance;
936 if( bAutoMaximum )
938 // round to entire multiples of the distance, based on the base value
939 if( m_bExpandBorderToIncrementRhythm )
940 fAxisMaximum = EquidistantTickFactory::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement );
941 // additional space, if source maximum is to near at axis maximum
942 if( m_bExpandIfValuesCloseToBorder )
943 if( (fAxisMaximum != 0.0) && ((fSourceMaximum - fAxisMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) )
944 fAxisMaximum += rExplicitIncrement.Distance;
947 // set the resulting limits (swap back to negative range if needed)
948 if( bSwapAndNegateRange )
950 rExplicitScale.Minimum = -fAxisMaximum;
951 rExplicitScale.Maximum = -fAxisMinimum;
953 else
955 rExplicitScale.Minimum = fAxisMinimum;
956 rExplicitScale.Maximum = fAxisMaximum;
959 /* If the number of intervals is too high (e.g. due to invalid fixed
960 distance or due to added space above or below data points),
961 calculate again with increased distance. */
962 double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance );
963 bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount;
964 // if manual distance is invalid, trigger automatic calculation
965 if( bNeedIteration )
966 bAutoDistance = true;
969 //fill explicit sub increment
970 sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength();
971 for( sal_Int32 nN=0; nN<nSubCount; nN++ )
973 ExplicitSubIncrement aExplicitSubIncrement;
974 const SubIncrement& rSubIncrement= m_aSourceScale.IncrementData.SubIncrements[nN];
975 if(!(rSubIncrement.IntervalCount>>=aExplicitSubIncrement.IntervalCount))
977 //scaling dependent
978 //@todo autocalculate IntervalCount dependent on MainIncrement and scaling
979 aExplicitSubIncrement.IntervalCount = 2;
981 lcl_ensureMaximumSubIncrementCount( aExplicitSubIncrement.IntervalCount );
982 if(!(rSubIncrement.PostEquidistant>>=aExplicitSubIncrement.PostEquidistant))
984 //scaling dependent
985 aExplicitSubIncrement.PostEquidistant = false;
987 rExplicitIncrement.SubIncrements.push_back(aExplicitSubIncrement);
991 } //namespace chart
993 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */