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