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 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 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 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 85 ExplicitSubIncrement::ExplicitSubIncrement() 86 : IntervalCount(2) 87 , PostEquidistant(true) 88 { 89 } 90 91 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 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 } 123 ScaleAutomatism::~ScaleAutomatism() 124 { 125 } 126 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 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 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 161 void ScaleAutomatism::setAutomaticTimeResolution( sal_Int32 nTimeResolution ) 162 { 163 m_nTimeResolution = nTimeResolution; 164 } 165 166 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 246 ScaleData ScaleAutomatism::getScale() const 247 { 248 return m_aSourceScale; 249 } 250 251 Date ScaleAutomatism::getNullDate() const 252 { 253 return m_aNullDate; 254 } 255 256 // private -------------------------------------------------------------------- 257 258 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 318 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 582 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 777 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