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 <basegfx/numeric/ftools.hxx> 27 28 #include "VCartesianAxis.hxx" 29 #include "PlottingPositionHelper.hxx" 30 #include "ShapeFactory.hxx" 31 #include "CommonConverters.hxx" 32 #include "macros.hxx" 33 #include "ViewDefines.hxx" 34 #include "PropertyMapper.hxx" 35 #include "NumberFormatterWrapper.hxx" 36 #include "LabelPositionHelper.hxx" 37 #include "TrueGuard.hxx" 38 #include "BaseGFXHelper.hxx" 39 #include "AxisHelper.hxx" 40 #include "Tickmarks_Equidistant.hxx" 41 42 #include <rtl/math.hxx> 43 #include <tools/color.hxx> 44 #include <tools/debug.hxx> 45 #include <com/sun/star/text/XText.hpp> 46 #include <com/sun/star/text/WritingMode2.hpp> 47 #include <editeng/unoprnms.hxx> 48 #include <svx/unoshape.hxx> 49 #include <svx/unoshtxt.hxx> 50 51 #include <algorithm> 52 #include <memory> 53 54 //............................................................................. 55 namespace chart 56 { 57 //............................................................................. 58 using namespace ::com::sun::star; 59 using namespace ::com::sun::star::chart2; 60 using namespace ::rtl::math; 61 using ::com::sun::star::uno::Reference; 62 63 //----------------------------------------------------------------------------- 64 //----------------------------------------------------------------------------- 65 //----------------------------------------------------------------------------- 66 67 VCartesianAxis::VCartesianAxis( const AxisProperties& rAxisProperties 68 , const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier 69 , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount 70 , PlottingPositionHelper* pPosHelper )//takes ownership 71 : VAxisBase( nDimensionIndex, nDimensionCount, rAxisProperties, xNumberFormatsSupplier ) 72 { 73 if( pPosHelper ) 74 m_pPosHelper = pPosHelper; 75 else 76 m_pPosHelper = new PlottingPositionHelper(); 77 } 78 79 VCartesianAxis::~VCartesianAxis() 80 { 81 delete m_pPosHelper; 82 m_pPosHelper = NULL; 83 } 84 85 //----------------------------------------------------------------------------- 86 //----------------------------------------------------------------------------- 87 88 Reference< drawing::XShape > createSingleLabel( 89 const Reference< lang::XMultiServiceFactory>& xShapeFactory 90 , const Reference< drawing::XShapes >& xTarget 91 , const awt::Point& rAnchorScreenPosition2D 92 , const rtl::OUString& rLabel 93 , const AxisLabelProperties& rAxisLabelProperties 94 , const AxisProperties& rAxisProperties 95 , const tNameSequence& rPropNames 96 , const tAnySequence& rPropValues 97 ) 98 { 99 if( rLabel.isEmpty() ) 100 return 0; 101 102 // #i78696# use mathematically correct rotation now 103 const double fRotationAnglePi(rAxisLabelProperties.fRotationAngleDegree * (F_PI / -180.0)); 104 uno::Any aATransformation = ShapeFactory::makeTransformation( rAnchorScreenPosition2D, fRotationAnglePi ); 105 rtl::OUString aLabel = ShapeFactory::getStackedString( rLabel, rAxisLabelProperties.bStackCharacters ); 106 107 Reference< drawing::XShape > xShape2DText = ShapeFactory(xShapeFactory) 108 .createText( xTarget, aLabel, rPropNames, rPropValues, aATransformation ); 109 110 //correctPositionForRotation 111 LabelPositionHelper::correctPositionForRotation( xShape2DText 112 , rAxisProperties.m_aLabelAlignment, rAxisLabelProperties.fRotationAngleDegree, rAxisProperties.m_bComplexCategories ); 113 114 return xShape2DText; 115 } 116 117 bool lcl_doesShapeOverlapWithTickmark( const Reference< drawing::XShape >& xShape 118 , double fRotationAngleDegree 119 , const basegfx::B2DVector& rTickScreenPosition 120 , bool bIsHorizontalAxis, bool bIsVerticalAxis ) 121 { 122 if(!xShape.is()) 123 return false; 124 125 ::basegfx::B2IRectangle aShapeRect = BaseGFXHelper::makeRectangle(xShape->getPosition(),ShapeFactory::getSizeAfterRotation( xShape, fRotationAngleDegree )); 126 127 if( bIsVerticalAxis ) 128 { 129 return ( (rTickScreenPosition.getY() >= aShapeRect.getMinY()) 130 && (rTickScreenPosition.getY() <= aShapeRect.getMaxY()) ); 131 } 132 if( bIsHorizontalAxis ) 133 { 134 return ( (rTickScreenPosition.getX() >= aShapeRect.getMinX()) 135 && (rTickScreenPosition.getX() <= aShapeRect.getMaxX()) ); 136 } 137 138 basegfx::B2IVector aPosition( 139 static_cast<sal_Int32>( rTickScreenPosition.getX() ) 140 , static_cast<sal_Int32>( rTickScreenPosition.getY() ) ); 141 return aShapeRect.isInside(aPosition); 142 } 143 144 bool doesOverlap( const Reference< drawing::XShape >& xShape1 145 , const Reference< drawing::XShape >& xShape2 146 , double fRotationAngleDegree ) 147 { 148 if( !xShape1.is() || !xShape2.is() ) 149 return false; 150 151 ::basegfx::B2IRectangle aRect1( BaseGFXHelper::makeRectangle(xShape1->getPosition(),ShapeFactory::getSizeAfterRotation( xShape1, fRotationAngleDegree ))); 152 ::basegfx::B2IRectangle aRect2( BaseGFXHelper::makeRectangle(xShape2->getPosition(),ShapeFactory::getSizeAfterRotation( xShape2, fRotationAngleDegree ))); 153 return aRect1.overlaps(aRect2); 154 } 155 156 void removeShapesAtWrongRhythm( TickIter& rIter 157 , sal_Int32 nCorrectRhythm 158 , sal_Int32 nMaxTickToCheck 159 , const Reference< drawing::XShapes >& xTarget ) 160 { 161 sal_Int32 nTick = 0; 162 for( TickInfo* pTickInfo = rIter.firstInfo() 163 ; pTickInfo && nTick <= nMaxTickToCheck 164 ; pTickInfo = rIter.nextInfo(), nTick++ ) 165 { 166 //remove labels which does not fit into the rhythm 167 if( nTick%nCorrectRhythm != 0) 168 { 169 if(pTickInfo->xTextShape.is()) 170 { 171 xTarget->remove(pTickInfo->xTextShape); 172 pTickInfo->xTextShape = NULL; 173 } 174 } 175 } 176 } 177 178 class LabelIterator : public TickIter 179 { 180 //this Iterator iterates over existing text labels 181 182 //if the labels are staggered and bInnerLine is true 183 //we iterate only through the labels which are lying more inside the diagram 184 185 //if the labels are staggered and bInnerLine is false 186 //we iterate only through the labels which are lying more outside the diagram 187 188 //if the labels are not staggered 189 //we iterate through all labels 190 191 public: 192 LabelIterator( ::std::vector< TickInfo >& rTickInfoVector 193 , const AxisLabelStaggering eAxisLabelStaggering 194 , bool bInnerLine ); 195 196 virtual TickInfo* firstInfo(); 197 virtual TickInfo* nextInfo(); 198 199 private: //methods 200 LabelIterator(); 201 202 private: //member 203 PureTickIter m_aPureTickIter; 204 const AxisLabelStaggering m_eAxisLabelStaggering; 205 bool m_bInnerLine; 206 }; 207 208 LabelIterator::LabelIterator( ::std::vector< TickInfo >& rTickInfoVector 209 , const AxisLabelStaggering eAxisLabelStaggering 210 , bool bInnerLine ) 211 : m_aPureTickIter( rTickInfoVector ) 212 , m_eAxisLabelStaggering(eAxisLabelStaggering) 213 , m_bInnerLine(bInnerLine) 214 { 215 } 216 217 TickInfo* LabelIterator::firstInfo() 218 { 219 TickInfo* pTickInfo = m_aPureTickIter.firstInfo(); 220 while( pTickInfo && !pTickInfo->xTextShape.is() ) 221 pTickInfo = m_aPureTickIter.nextInfo(); 222 if(!pTickInfo) 223 return NULL; 224 if( (STAGGER_EVEN==m_eAxisLabelStaggering && m_bInnerLine) 225 || 226 (STAGGER_ODD==m_eAxisLabelStaggering && !m_bInnerLine) 227 ) 228 { 229 //skip first label 230 do 231 pTickInfo = m_aPureTickIter.nextInfo(); 232 while( pTickInfo && !pTickInfo->xTextShape.is() ); 233 } 234 if(!pTickInfo) 235 return NULL; 236 return pTickInfo; 237 } 238 239 TickInfo* LabelIterator::nextInfo() 240 { 241 TickInfo* pTickInfo = NULL; 242 //get next label 243 do 244 pTickInfo = m_aPureTickIter.nextInfo(); 245 while( pTickInfo && !pTickInfo->xTextShape.is() ); 246 247 if( STAGGER_EVEN==m_eAxisLabelStaggering 248 || STAGGER_ODD==m_eAxisLabelStaggering ) 249 { 250 //skip one label 251 do 252 pTickInfo = m_aPureTickIter.nextInfo(); 253 while( pTickInfo && !pTickInfo->xTextShape.is() ); 254 } 255 return pTickInfo; 256 } 257 258 B2DVector lcl_getLabelsDistance( TickIter& rIter, const B2DVector& rDistanceTickToText, double fRotationAngleDegree ) 259 { 260 //calculates the height or width of a line of labels 261 //thus a following line of labels can be shifted for that distance 262 263 B2DVector aRet(0,0); 264 265 sal_Int32 nDistanceTickToText = static_cast<sal_Int32>( rDistanceTickToText.getLength() ); 266 if( nDistanceTickToText==0.0) 267 return aRet; 268 269 B2DVector aStaggerDirection(rDistanceTickToText); 270 aStaggerDirection.normalize(); 271 272 sal_Int32 nDistance=0; 273 Reference< drawing::XShape > xShape2DText(NULL); 274 for( TickInfo* pTickInfo = rIter.firstInfo() 275 ; pTickInfo 276 ; pTickInfo = rIter.nextInfo() ) 277 { 278 xShape2DText = pTickInfo->xTextShape; 279 if( xShape2DText.is() ) 280 { 281 awt::Size aSize = ShapeFactory::getSizeAfterRotation( xShape2DText, fRotationAngleDegree ); 282 if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY())) 283 nDistance = ::std::max(nDistance,aSize.Width); 284 else 285 nDistance = ::std::max(nDistance,aSize.Height); 286 } 287 } 288 289 aRet = aStaggerDirection*nDistance; 290 291 //add extra distance for vertical distance 292 if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY())) 293 aRet += rDistanceTickToText; 294 295 return aRet; 296 } 297 298 void lcl_shiftLables( TickIter& rIter, const B2DVector& rStaggerDistance ) 299 { 300 if(rStaggerDistance.getLength()==0.0) 301 return; 302 Reference< drawing::XShape > xShape2DText(NULL); 303 for( TickInfo* pTickInfo = rIter.firstInfo() 304 ; pTickInfo 305 ; pTickInfo = rIter.nextInfo() ) 306 { 307 xShape2DText = pTickInfo->xTextShape; 308 if( xShape2DText.is() ) 309 { 310 awt::Point aPos = xShape2DText->getPosition(); 311 aPos.X += static_cast<sal_Int32>(rStaggerDistance.getX()); 312 aPos.Y += static_cast<sal_Int32>(rStaggerDistance.getY()); 313 xShape2DText->setPosition( aPos ); 314 } 315 } 316 } 317 318 bool lcl_hasWordBreak( const Reference< drawing::XShape >& rxShape ) 319 { 320 if ( rxShape.is() ) 321 { 322 SvxShape* pShape = SvxShape::getImplementation( rxShape ); 323 SvxShapeText* pShapeText = dynamic_cast< SvxShapeText* >( pShape ); 324 if ( pShapeText ) 325 { 326 SvxTextEditSource* pTextEditSource = dynamic_cast< SvxTextEditSource* >( pShapeText->GetEditSource() ); 327 if ( pTextEditSource ) 328 { 329 pTextEditSource->UpdateOutliner(); 330 SvxTextForwarder* pTextForwarder = pTextEditSource->GetTextForwarder(); 331 if ( pTextForwarder ) 332 { 333 sal_uInt16 nParaCount = pTextForwarder->GetParagraphCount(); 334 for ( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara ) 335 { 336 sal_uInt16 nLineCount = pTextForwarder->GetLineCount( nPara ); 337 for ( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine ) 338 { 339 sal_uInt16 nLineStart = 0; 340 sal_uInt16 nLineEnd = 0; 341 pTextForwarder->GetLineBoundaries( nLineStart, nLineEnd, nPara, nLine ); 342 sal_uInt16 nWordStart = 0; 343 sal_uInt16 nWordEnd = 0; 344 if ( pTextForwarder->GetWordIndices( nPara, nLineStart, nWordStart, nWordEnd ) && 345 ( nWordStart != nLineStart ) ) 346 { 347 return true; 348 } 349 } 350 } 351 } 352 } 353 } 354 } 355 356 return false; 357 } 358 359 class MaxLabelTickIter : public TickIter 360 { 361 //iterate over first two and last two labels and the longest label 362 public: 363 MaxLabelTickIter( ::std::vector< TickInfo >& rTickInfoVector 364 , sal_Int32 nLongestLabelIndex ); 365 virtual ~MaxLabelTickIter(); 366 367 virtual TickInfo* firstInfo(); 368 virtual TickInfo* nextInfo(); 369 370 private: 371 ::std::vector< TickInfo >& m_rTickInfoVector; 372 ::std::vector< sal_Int32 > m_aValidIndices; 373 sal_Int32 m_nCurrentIndex; 374 }; 375 376 MaxLabelTickIter::MaxLabelTickIter( ::std::vector< TickInfo >& rTickInfoVector 377 , sal_Int32 nLongestLabelIndex ) 378 : m_rTickInfoVector(rTickInfoVector) 379 , m_nCurrentIndex(0) 380 { 381 sal_Int32 nMaxIndex = m_rTickInfoVector.size()-1; 382 if( nLongestLabelIndex<0 || nLongestLabelIndex>=nMaxIndex-1 ) 383 nLongestLabelIndex = 0; 384 385 if( nMaxIndex>=0 ) 386 m_aValidIndices.push_back(0); 387 if( nMaxIndex>=1 ) 388 m_aValidIndices.push_back(1); 389 if( nLongestLabelIndex>1 ) 390 m_aValidIndices.push_back(nLongestLabelIndex); 391 if( nMaxIndex > 2 ) 392 m_aValidIndices.push_back(nMaxIndex-1); 393 if( nMaxIndex > 1 ) 394 m_aValidIndices.push_back(nMaxIndex); 395 } 396 MaxLabelTickIter::~MaxLabelTickIter() 397 { 398 } 399 400 TickInfo* MaxLabelTickIter::firstInfo() 401 { 402 m_nCurrentIndex = 0; 403 if( m_nCurrentIndex < static_cast<sal_Int32>(m_aValidIndices.size()) ) 404 return &m_rTickInfoVector[m_aValidIndices[m_nCurrentIndex]]; 405 return 0; 406 } 407 408 TickInfo* MaxLabelTickIter::nextInfo() 409 { 410 m_nCurrentIndex++; 411 if( m_nCurrentIndex>=0 && m_nCurrentIndex<static_cast<sal_Int32>(m_aValidIndices.size()) ) 412 return &m_rTickInfoVector[m_aValidIndices[m_nCurrentIndex]]; 413 return 0; 414 } 415 416 bool VCartesianAxis::isBreakOfLabelsAllowed( const AxisLabelProperties& rAxisLabelProperties 417 , bool bIsHorizontalAxis ) 418 { 419 if( m_aTextLabels.getLength() > 100 ) 420 return false; 421 if( !rAxisLabelProperties.bLineBreakAllowed ) 422 return false; 423 if( rAxisLabelProperties.bStackCharacters ) 424 return false; 425 //no break for value axis 426 if( !m_bUseTextLabels ) 427 return false; 428 if( !::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) ) 429 return false; 430 //break only for horizontal axis 431 return bIsHorizontalAxis; 432 } 433 434 bool VCartesianAxis::isAutoStaggeringOfLabelsAllowed( const AxisLabelProperties& rAxisLabelProperties 435 , bool bIsHorizontalAxis, bool bIsVerticalAxis ) 436 { 437 if( rAxisLabelProperties.eStaggering != STAGGER_AUTO ) 438 return false; 439 if( rAxisLabelProperties.bOverlapAllowed ) 440 return false; 441 if( rAxisLabelProperties.bLineBreakAllowed ) //auto line break or auto staggering, doing both automatisms they may conflict... 442 return false; 443 if( !::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) ) 444 return false; 445 //automatic staggering only for horizontal axis with horizontal text 446 //or vertical axis with vertical text 447 if( bIsHorizontalAxis ) 448 return !rAxisLabelProperties.bStackCharacters; 449 if( bIsVerticalAxis ) 450 return rAxisLabelProperties.bStackCharacters; 451 return false; 452 } 453 454 struct ComplexCategoryPlacement 455 { 456 rtl::OUString Text; 457 sal_Int32 Count; 458 double TickValue; 459 460 ComplexCategoryPlacement( const rtl::OUString& rText, sal_Int32 nCount, double fTickValue ) 461 : Text(rText), Count(nCount), TickValue(fTickValue) 462 {} 463 }; 464 465 void VCartesianAxis::createAllTickInfosFromComplexCategories( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos, bool bShiftedPosition ) 466 { 467 //no minor tickmarks will be generated! 468 //order is: inner labels first , outer labels last (that is different to all other TickIter cases) 469 if(!bShiftedPosition) 470 { 471 rAllTickInfos.clear(); 472 sal_Int32 nLevel=0; 473 sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount(); 474 for( ; nLevel<nLevelCount; nLevel++ ) 475 { 476 ::std::vector< TickInfo > aTickInfoVector; 477 std::vector< ComplexCategory > aComplexCategories( m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel( nLevel ) ); 478 sal_Int32 nCatIndex = 0; 479 std::vector< ComplexCategory >::const_iterator aIt(aComplexCategories.begin()); 480 std::vector< ComplexCategory >::const_iterator aEnd(aComplexCategories.end()); 481 for(;aIt!=aEnd;++aIt) 482 { 483 TickInfo aTickInfo(0); 484 ComplexCategory aCat(*aIt); 485 sal_Int32 nCount = aCat.Count; 486 if( nCatIndex + 1.0 + nCount >= m_aScale.Maximum ) 487 { 488 nCount = static_cast<sal_Int32>(m_aScale.Maximum - 1.0 - nCatIndex); 489 if( nCount <= 0 ) 490 nCount = 1; 491 } 492 aTickInfo.fScaledTickValue = nCatIndex + 1.0 + nCount/2.0; 493 aTickInfo.nFactorForLimitedTextWidth = nCount; 494 aTickInfo.aText = aCat.Text; 495 aTickInfoVector.push_back(aTickInfo); 496 nCatIndex += nCount; 497 if( nCatIndex + 1.0 >= m_aScale.Maximum ) 498 break; 499 } 500 rAllTickInfos.push_back(aTickInfoVector); 501 } 502 } 503 else //bShiftedPosition==false 504 { 505 rAllTickInfos.clear(); 506 sal_Int32 nLevel=0; 507 sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount(); 508 for( ; nLevel<nLevelCount; nLevel++ ) 509 { 510 ::std::vector< TickInfo > aTickInfoVector; 511 std::vector< ComplexCategory > aComplexCategories( m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel( nLevel ) ); 512 sal_Int32 nCatIndex = 0; 513 std::vector< ComplexCategory >::const_iterator aIt(aComplexCategories.begin()); 514 std::vector< ComplexCategory >::const_iterator aEnd(aComplexCategories.end()); 515 for(;aIt!=aEnd;++aIt) 516 { 517 TickInfo aTickInfo(0); 518 ComplexCategory aCat(*aIt); 519 aTickInfo.fScaledTickValue = nCatIndex + 1.0; 520 aTickInfoVector.push_back(aTickInfo); 521 nCatIndex += aCat.Count; 522 if( nCatIndex + 1.0 > m_aScale.Maximum ) 523 break; 524 } 525 //fill up with single ticks until maximum scale 526 while( nCatIndex + 1.0 < m_aScale.Maximum ) 527 { 528 TickInfo aTickInfo(0); 529 aTickInfo.fScaledTickValue = nCatIndex + 1.0; 530 aTickInfoVector.push_back(aTickInfo); 531 nCatIndex ++; 532 if( nLevel>0 ) 533 break; 534 } 535 //add an additional tick at the end 536 { 537 TickInfo aTickInfo(0); 538 aTickInfo.fScaledTickValue = m_aScale.Maximum; 539 aTickInfoVector.push_back(aTickInfo); 540 } 541 rAllTickInfos.push_back(aTickInfoVector); 542 } 543 } 544 } 545 546 void VCartesianAxis::createAllTickInfos( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) 547 { 548 if( isComplexCategoryAxis() ) 549 createAllTickInfosFromComplexCategories( rAllTickInfos, false ); 550 else 551 VAxisBase::createAllTickInfos(rAllTickInfos); 552 } 553 554 ::std::auto_ptr< TickIter > VCartesianAxis::createLabelTickIterator( sal_Int32 nTextLevel ) 555 { 556 if( nTextLevel>=0 && nTextLevel < static_cast< sal_Int32 >(m_aAllTickInfos.size()) ) 557 return ::std::auto_ptr< TickIter >( new PureTickIter( m_aAllTickInfos[nTextLevel] ) ); 558 return ::std::auto_ptr< TickIter >(); 559 } 560 ::std::auto_ptr< TickIter > VCartesianAxis::createMaximumLabelTickIterator( sal_Int32 nTextLevel ) 561 { 562 if( isComplexCategoryAxis() || isDateAxis() ) 563 { 564 return createLabelTickIterator( nTextLevel ); //mmmm maybe todo: create less than all texts here 565 } 566 else 567 { 568 if(nTextLevel==0) 569 { 570 if( !m_aAllTickInfos.empty() ) 571 { 572 sal_Int32 nLongestLabelIndex = m_bUseTextLabels ? this->getIndexOfLongestLabel( m_aTextLabels ) : 0; 573 return ::std::auto_ptr< TickIter >( new MaxLabelTickIter( m_aAllTickInfos[0], nLongestLabelIndex ) ); 574 } 575 } 576 } 577 return ::std::auto_ptr< TickIter >(); 578 } 579 580 sal_Int32 VCartesianAxis::getTextLevelCount() const 581 { 582 sal_Int32 nTextLevelCount = 1; 583 if( isComplexCategoryAxis() ) 584 nTextLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount(); 585 return nTextLevelCount; 586 } 587 588 bool VCartesianAxis::createTextShapes( 589 const Reference< drawing::XShapes >& xTarget 590 , TickIter& rTickIter 591 , AxisLabelProperties& rAxisLabelProperties 592 , TickFactory_2D* pTickFactory 593 , sal_Int32 nScreenDistanceBetweenTicks ) 594 { 595 //returns true if the text shapes have been created succesfully 596 //otherwise false - in this case the AxisLabelProperties have changed 597 //and contain new instructions for the next try for text shape creation 598 599 Reference< XScaling > xInverseScaling( NULL ); 600 if( m_aScale.Scaling.is() ) 601 xInverseScaling = m_aScale.Scaling->getInverseScaling(); 602 603 FixedNumberFormatter aFixedNumberFormatter( 604 m_xNumberFormatsSupplier, rAxisLabelProperties.nNumberFormatKey ); 605 606 const bool bIsHorizontalAxis = pTickFactory->isHorizontalAxis(); 607 const bool bIsVerticalAxis = pTickFactory->isVerticalAxis(); 608 bool bIsStaggered = rAxisLabelProperties.getIsStaggered(); 609 B2DVector aTextToTickDistance( pTickFactory->getDistanceAxisTickToText( m_aAxisProperties, true ) ); 610 sal_Int32 nLimitedSpaceForText = -1; 611 if( isBreakOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis ) ) 612 { 613 nLimitedSpaceForText = nScreenDistanceBetweenTicks; 614 if( bIsStaggered ) 615 nLimitedSpaceForText *= 2; 616 617 if( nLimitedSpaceForText > 0 ) 618 { //reduce space for a small amount to have a visible distance between the labels: 619 sal_Int32 nReduce = (nLimitedSpaceForText*5)/100; 620 if(!nReduce) 621 nReduce = 1; 622 nLimitedSpaceForText -= nReduce; 623 } 624 } 625 626 std::vector< ComplexCategoryPlacement > aComplexCategoryPlacements; 627 uno::Sequence< rtl::OUString >* pCategories = 0; 628 if( m_bUseTextLabels && !m_aAxisProperties.m_bComplexCategories ) 629 pCategories = &m_aTextLabels; 630 631 TickInfo* pPreviousVisibleTickInfo = NULL; 632 TickInfo* pPREPreviousVisibleTickInfo = NULL; 633 TickInfo* pLastVisibleNeighbourTickInfo = NULL; 634 635 //------------------------------------------------ 636 //prepare properties for multipropertyset-interface of shape 637 tNameSequence aPropNames; 638 tAnySequence aPropValues; 639 640 bool bLimitedHeight = fabs(aTextToTickDistance.getX()) > fabs(aTextToTickDistance.getY()); 641 Reference< beans::XPropertySet > xProps( m_aAxisProperties.m_xAxisModel, uno::UNO_QUERY ); 642 PropertyMapper::getTextLabelMultiPropertyLists( xProps, aPropNames, aPropValues, false 643 , nLimitedSpaceForText, bLimitedHeight ); 644 LabelPositionHelper::doDynamicFontResize( aPropValues, aPropNames, xProps 645 , m_aAxisLabelProperties.m_aFontReferenceSize ); 646 LabelPositionHelper::changeTextAdjustment( aPropValues, aPropNames, m_aAxisProperties.m_aLabelAlignment ); 647 648 uno::Any* pColorAny = PropertyMapper::getValuePointer(aPropValues,aPropNames,C2U("CharColor")); 649 sal_Int32 nColor = Color( COL_AUTO ).GetColor(); 650 if(pColorAny) 651 *pColorAny >>= nColor; 652 653 uno::Any* pLimitedSpaceAny = PropertyMapper::getValuePointerForLimitedSpace(aPropValues,aPropNames,bLimitedHeight); 654 //------------------------------------------------ 655 656 sal_Int32 nTick = 0; 657 for( TickInfo* pTickInfo = rTickIter.firstInfo() 658 ; pTickInfo 659 ; pTickInfo = rTickIter.nextInfo(), nTick++ ) 660 { 661 pLastVisibleNeighbourTickInfo = bIsStaggered ? 662 pPREPreviousVisibleTickInfo : pPreviousVisibleTickInfo; 663 664 //don't create labels which does not fit into the rhythm 665 if( nTick%rAxisLabelProperties.nRhythm != 0) 666 continue; 667 668 //don't create labels for invisible ticks 669 if( !pTickInfo->bPaintIt ) 670 continue; 671 672 //if NO OVERLAP -> don't create labels where the tick overlaps 673 //with the text of the last neighbour tickmark 674 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed ) 675 { 676 if( lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape 677 , rAxisLabelProperties.fRotationAngleDegree 678 , pTickInfo->aTickScreenPosition 679 , bIsHorizontalAxis, bIsVerticalAxis ) ) 680 { 681 bool bOverlapAlsoAfterSwitchingOnAutoStaggering = true; 682 if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) ) 683 { 684 bIsStaggered = true; 685 rAxisLabelProperties.eStaggering = STAGGER_EVEN; 686 pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo; 687 if( !pLastVisibleNeighbourTickInfo || 688 !lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape 689 , rAxisLabelProperties.fRotationAngleDegree 690 , pTickInfo->aTickScreenPosition 691 , bIsHorizontalAxis, bIsVerticalAxis ) ) 692 bOverlapAlsoAfterSwitchingOnAutoStaggering = false; 693 } 694 if( bOverlapAlsoAfterSwitchingOnAutoStaggering ) 695 { 696 if( rAxisLabelProperties.bRhythmIsFix ) 697 continue; 698 rAxisLabelProperties.nRhythm++; 699 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget ); 700 return false; 701 } 702 } 703 } 704 705 //xxxxx pTickInfo->updateUnscaledValue( xInverseScaling ); 706 707 bool bHasExtraColor=false; 708 sal_Int32 nExtraColor=0; 709 710 rtl::OUString aLabel; 711 if(pCategories) 712 { 713 sal_Int32 nIndex = static_cast< sal_Int32 >(pTickInfo->getUnscaledTickValue()) - 1; //first category (index 0) matches with real number 1.0 714 if( nIndex>=0 && nIndex<pCategories->getLength() ) 715 aLabel = (*pCategories)[nIndex]; 716 } 717 else if( m_aAxisProperties.m_bComplexCategories ) 718 { 719 aLabel = pTickInfo->aText; 720 } 721 else 722 aLabel = aFixedNumberFormatter.getFormattedString( pTickInfo->getUnscaledTickValue(), nExtraColor, bHasExtraColor ); 723 724 if(pColorAny) 725 *pColorAny = uno::makeAny(bHasExtraColor?nExtraColor:nColor); 726 if(pLimitedSpaceAny) 727 *pLimitedSpaceAny = uno::makeAny(sal_Int32(nLimitedSpaceForText*pTickInfo->nFactorForLimitedTextWidth)); 728 729 B2DVector aTickScreenPos2D( pTickInfo->aTickScreenPosition ); 730 aTickScreenPos2D += aTextToTickDistance; 731 awt::Point aAnchorScreenPosition2D( 732 static_cast<sal_Int32>(aTickScreenPos2D.getX()) 733 ,static_cast<sal_Int32>(aTickScreenPos2D.getY())); 734 735 //create single label 736 if(!pTickInfo->xTextShape.is()) 737 pTickInfo->xTextShape = createSingleLabel( m_xShapeFactory, xTarget 738 , aAnchorScreenPosition2D, aLabel 739 , rAxisLabelProperties, m_aAxisProperties 740 , aPropNames, aPropValues ); 741 if(!pTickInfo->xTextShape.is()) 742 continue; 743 744 recordMaximumTextSize( pTickInfo->xTextShape, rAxisLabelProperties.fRotationAngleDegree ); 745 746 //better rotate if single words are broken apart 747 if( nLimitedSpaceForText>0 && !rAxisLabelProperties.bOverlapAllowed 748 && ::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) 749 && m_aAxisProperties.m_bComplexCategories 750 && lcl_hasWordBreak( pTickInfo->xTextShape ) ) 751 { 752 rAxisLabelProperties.fRotationAngleDegree = 90; 753 rAxisLabelProperties.bLineBreakAllowed = false; 754 m_aAxisLabelProperties.fRotationAngleDegree = rAxisLabelProperties.fRotationAngleDegree; 755 removeTextShapesFromTicks(); 756 return false; 757 } 758 759 //if NO OVERLAP -> remove overlapping shapes 760 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed ) 761 { 762 if( doesOverlap( pLastVisibleNeighbourTickInfo->xTextShape, pTickInfo->xTextShape, rAxisLabelProperties.fRotationAngleDegree ) ) 763 { 764 bool bOverlapAlsoAfterSwitchingOnAutoStaggering = true; 765 if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) ) 766 { 767 bIsStaggered = true; 768 rAxisLabelProperties.eStaggering = STAGGER_EVEN; 769 pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo; 770 if( !pLastVisibleNeighbourTickInfo || 771 !lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape 772 , rAxisLabelProperties.fRotationAngleDegree 773 , pTickInfo->aTickScreenPosition 774 , bIsHorizontalAxis, bIsVerticalAxis ) ) 775 bOverlapAlsoAfterSwitchingOnAutoStaggering = false; 776 } 777 if( bOverlapAlsoAfterSwitchingOnAutoStaggering ) 778 { 779 if( rAxisLabelProperties.bRhythmIsFix ) 780 { 781 xTarget->remove(pTickInfo->xTextShape); 782 pTickInfo->xTextShape = NULL; 783 continue; 784 } 785 rAxisLabelProperties.nRhythm++; 786 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget ); 787 return false; 788 } 789 } 790 } 791 792 pPREPreviousVisibleTickInfo = pPreviousVisibleTickInfo; 793 pPreviousVisibleTickInfo = pTickInfo; 794 } 795 return true; 796 } 797 798 drawing::PointSequenceSequence lcl_makePointSequence( B2DVector& rStart, B2DVector& rEnd ) 799 { 800 drawing::PointSequenceSequence aPoints(1); 801 aPoints[0].realloc(2); 802 aPoints[0][0].X = static_cast<sal_Int32>(rStart.getX()); 803 aPoints[0][0].Y = static_cast<sal_Int32>(rStart.getY()); 804 aPoints[0][1].X = static_cast<sal_Int32>(rEnd.getX()); 805 aPoints[0][1].Y = static_cast<sal_Int32>(rEnd.getY()); 806 return aPoints; 807 } 808 809 double VCartesianAxis::getLogicValueWhereMainLineCrossesOtherAxis() const 810 { 811 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY(); 812 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY(); 813 814 double fCrossesOtherAxis; 815 if(m_aAxisProperties.m_pfMainLinePositionAtOtherAxis) 816 fCrossesOtherAxis = *m_aAxisProperties.m_pfMainLinePositionAtOtherAxis; 817 else 818 { 819 if( ::com::sun::star::chart::ChartAxisPosition_END == m_aAxisProperties.m_eCrossoverType ) 820 fCrossesOtherAxis = fMax; 821 else 822 fCrossesOtherAxis = fMin; 823 } 824 return fCrossesOtherAxis; 825 } 826 827 double VCartesianAxis::getLogicValueWhereLabelLineCrossesOtherAxis() const 828 { 829 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY(); 830 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY(); 831 832 double fCrossesOtherAxis; 833 if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_START == m_aAxisProperties.m_eLabelPos ) 834 fCrossesOtherAxis = fMin; 835 else if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END == m_aAxisProperties.m_eLabelPos ) 836 fCrossesOtherAxis = fMax; 837 else 838 fCrossesOtherAxis = getLogicValueWhereMainLineCrossesOtherAxis(); 839 return fCrossesOtherAxis; 840 } 841 842 bool VCartesianAxis::getLogicValueWhereExtraLineCrossesOtherAxis( double& fCrossesOtherAxis ) const 843 { 844 if( !m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis ) 845 return false; 846 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY(); 847 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY(); 848 if( *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis <= fMin 849 || *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis >= fMax ) 850 return false; 851 fCrossesOtherAxis = *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis; 852 return true; 853 } 854 855 B2DVector VCartesianAxis::getScreenPosition( double fLogicX, double fLogicY, double fLogicZ ) const 856 { 857 B2DVector aRet(0,0); 858 859 if( m_pPosHelper ) 860 { 861 drawing::Position3D aScenePos = m_pPosHelper->transformLogicToScene( fLogicX, fLogicY, fLogicZ, true ); 862 if(3==m_nDimension) 863 { 864 if( m_xLogicTarget.is() && m_pPosHelper && m_pShapeFactory ) 865 { 866 tPropertyNameMap aDummyPropertyNameMap; 867 Reference< drawing::XShape > xShape3DAnchor = m_pShapeFactory->createCube( m_xLogicTarget 868 , aScenePos,drawing::Direction3D(1,1,1), 0, 0, aDummyPropertyNameMap); 869 awt::Point a2DPos = xShape3DAnchor->getPosition(); //get 2D position from xShape3DAnchor 870 m_xLogicTarget->remove(xShape3DAnchor); 871 aRet.setX( a2DPos.X ); 872 aRet.setY( a2DPos.Y ); 873 } 874 else 875 { 876 DBG_ERROR("cannot calculate scrren position in VCartesianAxis::getScreenPosition"); 877 } 878 } 879 else 880 { 881 aRet.setX( aScenePos.PositionX ); 882 aRet.setY( aScenePos.PositionY ); 883 } 884 } 885 886 return aRet; 887 } 888 889 VCartesianAxis::ScreenPosAndLogicPos VCartesianAxis::getScreenPosAndLogicPos( double fLogicX_, double fLogicY_, double fLogicZ_ ) const 890 { 891 ScreenPosAndLogicPos aRet; 892 aRet.fLogicX = fLogicX_; 893 aRet.fLogicY = fLogicY_; 894 aRet.fLogicZ = fLogicZ_; 895 aRet.aScreenPos = getScreenPosition( fLogicX_, fLogicY_, fLogicZ_ ); 896 return aRet; 897 } 898 899 typedef ::std::vector< VCartesianAxis::ScreenPosAndLogicPos > tScreenPosAndLogicPosList; 900 struct lcl_LessXPos : ::std::binary_function< VCartesianAxis::ScreenPosAndLogicPos, VCartesianAxis::ScreenPosAndLogicPos, bool > 901 { 902 inline bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 ) 903 { 904 return ( rPos1.aScreenPos.getX() < rPos2.aScreenPos.getX() ); 905 } 906 }; 907 908 struct lcl_GreaterYPos : ::std::binary_function< VCartesianAxis::ScreenPosAndLogicPos, VCartesianAxis::ScreenPosAndLogicPos, bool > 909 { 910 inline bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 ) 911 { 912 return ( rPos1.aScreenPos.getY() > rPos2.aScreenPos.getY() ); 913 } 914 }; 915 916 void VCartesianAxis::get2DAxisMainLine( B2DVector& rStart, B2DVector& rEnd, double fCrossesOtherAxis ) 917 { 918 //m_aAxisProperties might get updated and changed here because 919 // the label alignmant and inner direction sign depends exactly of the choice of the axis line position which is made here in this method 920 921 double fMinX = m_pPosHelper->getLogicMinX(); 922 double fMinY = m_pPosHelper->getLogicMinY(); 923 double fMinZ = m_pPosHelper->getLogicMinZ(); 924 double fMaxX = m_pPosHelper->getLogicMaxX(); 925 double fMaxY = m_pPosHelper->getLogicMaxY(); 926 double fMaxZ = m_pPosHelper->getLogicMaxZ(); 927 928 double fXStart = fMinX; 929 double fYStart = fMinY; 930 double fZStart = fMinZ; 931 double fXEnd = fXStart; 932 double fYEnd = fYStart; 933 double fZEnd = fZStart; 934 935 double fXOnXPlane = fMinX; 936 double fXOther = fMaxX; 937 int nDifferentValue = !m_pPosHelper->isMathematicalOrientationX() ? -1 : 1; 938 if( !m_pPosHelper->isSwapXAndY() ) 939 nDifferentValue *= (CuboidPlanePosition_Left != m_eLeftWallPos) ? -1 : 1; 940 else 941 nDifferentValue *= (CuboidPlanePosition_Bottom != m_eBottomPos) ? -1 : 1; 942 if( nDifferentValue<0 ) 943 { 944 fXOnXPlane = fMaxX; 945 fXOther = fMinX; 946 } 947 948 double fYOnYPlane = fMinY; 949 double fYOther = fMaxY; 950 nDifferentValue = !m_pPosHelper->isMathematicalOrientationY() ? -1 : 1; 951 if( !m_pPosHelper->isSwapXAndY() ) 952 nDifferentValue *= (CuboidPlanePosition_Bottom != m_eBottomPos) ? -1 : 1; 953 else 954 nDifferentValue *= (CuboidPlanePosition_Left != m_eLeftWallPos) ? -1 : 1; 955 if( nDifferentValue<0 ) 956 { 957 fYOnYPlane = fMaxY; 958 fYOther = fMinY; 959 } 960 961 double fZOnZPlane = fMaxZ; 962 double fZOther = fMinZ; 963 nDifferentValue = !m_pPosHelper->isMathematicalOrientationZ() ? -1 : 1; 964 nDifferentValue *= (CuboidPlanePosition_Back != m_eBackWallPos) ? -1 : 1; 965 if( nDifferentValue<0 ) 966 { 967 fZOnZPlane = fMinZ; 968 fZOther = fMaxZ; 969 } 970 971 if( 0==m_nDimensionIndex ) //x-axis 972 { 973 if( fCrossesOtherAxis < fMinY ) 974 fCrossesOtherAxis = fMinY; 975 else if( fCrossesOtherAxis > fMaxY ) 976 fCrossesOtherAxis = fMaxY; 977 978 fYStart = fYEnd = fCrossesOtherAxis; 979 fXEnd=m_pPosHelper->getLogicMaxX(); 980 981 if(3==m_nDimension) 982 { 983 if( AxisHelper::isAxisPositioningEnabled() ) 984 { 985 if( ::rtl::math::approxEqual( fYOther, fYStart) ) 986 fZStart = fZEnd = fZOnZPlane; 987 else 988 fZStart = fZEnd = fZOther; 989 } 990 else 991 { 992 rStart = getScreenPosition( fXStart, fYStart, fZStart ); 993 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); 994 995 double fDeltaX = rEnd.getX() - rStart.getX(); 996 double fDeltaY = rEnd.getY() - rStart.getY(); 997 998 //only those points are candidates which are lying on exactly one wall as these are outer edges 999 tScreenPosAndLogicPosList aPosList; 1000 aPosList.push_back( getScreenPosAndLogicPos( fMinX, fYOnYPlane, fZOther ) ); 1001 aPosList.push_back( getScreenPosAndLogicPos( fMinX, fYOther, fZOnZPlane ) ); 1002 1003 if( fabs(fDeltaY) > fabs(fDeltaX) ) 1004 { 1005 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_LEFT; 1006 //choose most left positions 1007 ::std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() ); 1008 m_aAxisProperties.m_fLabelDirectionSign = fDeltaY<0 ? -1 : 1; 1009 } 1010 else 1011 { 1012 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_BOTTOM; 1013 //choose most bottom positions 1014 ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() ); 1015 m_aAxisProperties.m_fLabelDirectionSign = fDeltaX<0 ? -1 : 1; 1016 } 1017 ScreenPosAndLogicPos aBestPos( aPosList[0] ); 1018 fYStart = fYEnd = aBestPos.fLogicY; 1019 fZStart = fZEnd = aBestPos.fLogicZ; 1020 if( !m_pPosHelper->isMathematicalOrientationX() ) 1021 m_aAxisProperties.m_fLabelDirectionSign *= -1; 1022 } 1023 }//end 3D x axis 1024 } 1025 else if( 1==m_nDimensionIndex ) //y-axis 1026 { 1027 if( fCrossesOtherAxis < fMinX ) 1028 fCrossesOtherAxis = fMinX; 1029 else if( fCrossesOtherAxis > fMaxX ) 1030 fCrossesOtherAxis = fMaxX; 1031 1032 fXStart = fXEnd = fCrossesOtherAxis; 1033 fYEnd=m_pPosHelper->getLogicMaxY(); 1034 1035 if(3==m_nDimension) 1036 { 1037 if( AxisHelper::isAxisPositioningEnabled() ) 1038 { 1039 if( ::rtl::math::approxEqual( fXOther, fXStart) ) 1040 fZStart = fZEnd = fZOnZPlane; 1041 else 1042 fZStart = fZEnd = fZOther; 1043 } 1044 else 1045 { 1046 rStart = getScreenPosition( fXStart, fYStart, fZStart ); 1047 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); 1048 1049 double fDeltaX = rEnd.getX() - rStart.getX(); 1050 double fDeltaY = rEnd.getY() - rStart.getY(); 1051 1052 //only those points are candidates which are lying on exactly one wall as these are outer edges 1053 tScreenPosAndLogicPosList aPosList; 1054 aPosList.push_back( getScreenPosAndLogicPos( fXOnXPlane, fMinY, fZOther ) ); 1055 aPosList.push_back( getScreenPosAndLogicPos( fXOther, fMinY, fZOnZPlane ) ); 1056 1057 if( fabs(fDeltaY) > fabs(fDeltaX) ) 1058 { 1059 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_LEFT; 1060 //choose most left positions 1061 ::std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() ); 1062 m_aAxisProperties.m_fLabelDirectionSign = fDeltaY<0 ? -1 : 1; 1063 } 1064 else 1065 { 1066 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_BOTTOM; 1067 //choose most bottom positions 1068 ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() ); 1069 m_aAxisProperties.m_fLabelDirectionSign = fDeltaX<0 ? -1 : 1; 1070 } 1071 ScreenPosAndLogicPos aBestPos( aPosList[0] ); 1072 fXStart = fXEnd = aBestPos.fLogicX; 1073 fZStart = fZEnd = aBestPos.fLogicZ; 1074 if( !m_pPosHelper->isMathematicalOrientationY() ) 1075 m_aAxisProperties.m_fLabelDirectionSign *= -1; 1076 } 1077 }//end 3D y axis 1078 } 1079 else //z-axis 1080 { 1081 fZEnd = m_pPosHelper->getLogicMaxZ(); 1082 if( AxisHelper::isAxisPositioningEnabled() ) 1083 { 1084 if( !m_aAxisProperties.m_bSwapXAndY ) 1085 { 1086 if( fCrossesOtherAxis < fMinY ) 1087 fCrossesOtherAxis = fMinY; 1088 else if( fCrossesOtherAxis > fMaxY ) 1089 fCrossesOtherAxis = fMaxY; 1090 fYStart = fYEnd = fCrossesOtherAxis; 1091 1092 if( ::rtl::math::approxEqual( fYOther, fYStart) ) 1093 fXStart = fXEnd = fXOnXPlane; 1094 else 1095 fXStart = fXEnd = fXOther; 1096 } 1097 else 1098 { 1099 if( fCrossesOtherAxis < fMinX ) 1100 fCrossesOtherAxis = fMinX; 1101 else if( fCrossesOtherAxis > fMaxX ) 1102 fCrossesOtherAxis = fMaxX; 1103 fXStart = fXEnd = fCrossesOtherAxis; 1104 1105 if( ::rtl::math::approxEqual( fXOther, fXStart) ) 1106 fYStart = fYEnd = fYOnYPlane; 1107 else 1108 fYStart = fYEnd = fYOther; 1109 } 1110 } 1111 else 1112 { 1113 if( !m_pPosHelper->isSwapXAndY() ) 1114 { 1115 fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMinX(); 1116 fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMinY() : m_pPosHelper->getLogicMaxY(); 1117 } 1118 else 1119 { 1120 fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMaxX(); 1121 fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMaxY() : m_pPosHelper->getLogicMinY(); 1122 } 1123 1124 if(3==m_nDimension) 1125 { 1126 rStart = getScreenPosition( fXStart, fYStart, fZStart ); 1127 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); 1128 1129 double fDeltaX = rEnd.getX() - rStart.getX(); 1130 1131 //only those points are candidates which are lying on exactly one wall as these are outer edges 1132 tScreenPosAndLogicPosList aPosList; 1133 aPosList.push_back( getScreenPosAndLogicPos( fXOther, fYOnYPlane, fMinZ ) ); 1134 aPosList.push_back( getScreenPosAndLogicPos( fXOnXPlane, fYOther, fMinZ ) ); 1135 1136 ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() ); 1137 ScreenPosAndLogicPos aBestPos( aPosList[0] ); 1138 ScreenPosAndLogicPos aNotSoGoodPos( aPosList[1] ); 1139 1140 //choose most bottom positions 1141 if( !::rtl::math::approxEqual( fDeltaX, 0.0 ) ) // prefere left-right algnments 1142 { 1143 if( aBestPos.aScreenPos.getX() > aNotSoGoodPos.aScreenPos.getX() ) 1144 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_RIGHT; 1145 else 1146 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_LEFT; 1147 } 1148 else 1149 { 1150 if( aBestPos.aScreenPos.getY() > aNotSoGoodPos.aScreenPos.getY() ) 1151 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_BOTTOM; 1152 else 1153 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_TOP; 1154 } 1155 1156 m_aAxisProperties.m_fLabelDirectionSign = fDeltaX<0 ? -1 : 1; 1157 if( !m_pPosHelper->isMathematicalOrientationZ() ) 1158 m_aAxisProperties.m_fLabelDirectionSign *= -1; 1159 1160 fXStart = fXEnd = aBestPos.fLogicX; 1161 fYStart = fYEnd = aBestPos.fLogicY; 1162 } 1163 }//end 3D z axis 1164 } 1165 1166 rStart = getScreenPosition( fXStart, fYStart, fZStart ); 1167 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); 1168 1169 if(3==m_nDimension && !AxisHelper::isAxisPositioningEnabled() ) 1170 m_aAxisProperties.m_fInnerDirectionSign = m_aAxisProperties.m_fLabelDirectionSign;//to behave like before 1171 1172 if(3==m_nDimension && AxisHelper::isAxisPositioningEnabled() ) 1173 { 1174 double fDeltaX = rEnd.getX() - rStart.getX(); 1175 double fDeltaY = rEnd.getY() - rStart.getY(); 1176 1177 if( 2==m_nDimensionIndex ) 1178 { 1179 if( m_eLeftWallPos != CuboidPlanePosition_Left ) 1180 { 1181 m_aAxisProperties.m_fLabelDirectionSign *= -1.0; 1182 m_aAxisProperties.m_fInnerDirectionSign *= -1.0; 1183 } 1184 1185 m_aAxisProperties.m_aLabelAlignment = 1186 ( m_aAxisProperties.m_fLabelDirectionSign<0 ) ? 1187 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; 1188 1189 if( ( fDeltaY<0 && m_aScale.Orientation == AxisOrientation_REVERSE ) || 1190 ( fDeltaY>0 && m_aScale.Orientation == AxisOrientation_MATHEMATICAL ) ) 1191 m_aAxisProperties.m_aLabelAlignment = 1192 ( m_aAxisProperties.m_aLabelAlignment==LABEL_ALIGN_RIGHT ) ? 1193 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; 1194 } 1195 else if( fabs(fDeltaY) > fabs(fDeltaX) ) 1196 { 1197 if( m_eBackWallPos != CuboidPlanePosition_Back ) 1198 { 1199 m_aAxisProperties.m_fLabelDirectionSign *= -1.0; 1200 m_aAxisProperties.m_fInnerDirectionSign *= -1.0; 1201 } 1202 1203 m_aAxisProperties.m_aLabelAlignment = 1204 ( m_aAxisProperties.m_fLabelDirectionSign<0 ) ? 1205 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; 1206 1207 if( ( fDeltaY<0 && m_aScale.Orientation == AxisOrientation_REVERSE ) || 1208 ( fDeltaY>0 && m_aScale.Orientation == AxisOrientation_MATHEMATICAL ) ) 1209 m_aAxisProperties.m_aLabelAlignment = 1210 ( m_aAxisProperties.m_aLabelAlignment==LABEL_ALIGN_RIGHT ) ? 1211 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; 1212 } 1213 else 1214 { 1215 if( m_eBackWallPos != CuboidPlanePosition_Back ) 1216 { 1217 m_aAxisProperties.m_fLabelDirectionSign *= -1.0; 1218 m_aAxisProperties.m_fInnerDirectionSign *= -1.0; 1219 } 1220 1221 m_aAxisProperties.m_aLabelAlignment = 1222 ( m_aAxisProperties.m_fLabelDirectionSign<0 ) ? 1223 LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM; 1224 1225 if( ( fDeltaX>0 && m_aScale.Orientation == AxisOrientation_REVERSE ) || 1226 ( fDeltaX<0 && m_aScale.Orientation == AxisOrientation_MATHEMATICAL ) ) 1227 m_aAxisProperties.m_aLabelAlignment = 1228 ( m_aAxisProperties.m_aLabelAlignment==LABEL_ALIGN_TOP ) ? 1229 LABEL_ALIGN_BOTTOM : LABEL_ALIGN_TOP; 1230 } 1231 } 1232 } 1233 1234 TickFactory* VCartesianAxis::createTickFactory() 1235 { 1236 return createTickFactory2D(); 1237 } 1238 1239 TickFactory_2D* VCartesianAxis::createTickFactory2D() 1240 { 1241 B2DVector aStart, aEnd; 1242 this->get2DAxisMainLine( aStart, aEnd, this->getLogicValueWhereMainLineCrossesOtherAxis() ); 1243 1244 B2DVector aLabelLineStart, aLabelLineEnd; 1245 this->get2DAxisMainLine( aLabelLineStart, aLabelLineEnd, this->getLogicValueWhereLabelLineCrossesOtherAxis() ); 1246 1247 return new TickFactory_2D( m_aScale, m_aIncrement, aStart, aEnd, aLabelLineStart-aStart ); 1248 } 1249 1250 void lcl_hideIdenticalScreenValues( TickIter& rTickIter ) 1251 { 1252 TickInfo* pPreviousTickInfo = rTickIter.firstInfo(); 1253 if(!pPreviousTickInfo) 1254 return; 1255 pPreviousTickInfo->bPaintIt = true; 1256 for( TickInfo* pTickInfo = rTickIter.nextInfo(); pTickInfo; pTickInfo = rTickIter.nextInfo()) 1257 { 1258 pTickInfo->bPaintIt = 1259 ( static_cast<sal_Int32>(pTickInfo->aTickScreenPosition.getX()) 1260 != static_cast<sal_Int32>(pPreviousTickInfo->aTickScreenPosition.getX()) ) 1261 || 1262 ( static_cast<sal_Int32>(pTickInfo->aTickScreenPosition.getY()) 1263 != static_cast<sal_Int32>(pPreviousTickInfo->aTickScreenPosition.getY()) ); 1264 pPreviousTickInfo = pTickInfo; 1265 } 1266 } 1267 1268 //'hide' tickmarks with identical screen values in aAllTickInfos 1269 void VCartesianAxis::hideIdenticalScreenValues( ::std::vector< ::std::vector< TickInfo > >& rTickInfos ) const 1270 { 1271 if( isComplexCategoryAxis() || isDateAxis() ) 1272 { 1273 sal_Int32 nCount = rTickInfos.size(); 1274 for( sal_Int32 nN=0; nN<nCount; nN++ ) 1275 { 1276 PureTickIter aTickIter( rTickInfos[nN] ); 1277 lcl_hideIdenticalScreenValues( aTickIter ); 1278 } 1279 } 1280 else 1281 { 1282 EquidistantTickIter aTickIter( rTickInfos, m_aIncrement, 0, -1 ); 1283 lcl_hideIdenticalScreenValues( aTickIter ); 1284 } 1285 } 1286 1287 sal_Int32 VCartesianAxis::estimateMaximumAutoMainIncrementCount() 1288 { 1289 sal_Int32 nRet = 10; 1290 1291 if( m_nMaximumTextWidthSoFar==0 && m_nMaximumTextHeightSoFar==0 ) 1292 return nRet; 1293 1294 B2DVector aStart, aEnd; 1295 this->get2DAxisMainLine( aStart, aEnd, this->getLogicValueWhereMainLineCrossesOtherAxis() ); 1296 1297 sal_Int32 nMaxHeight = static_cast<sal_Int32>(fabs(aEnd.getY()-aStart.getY())); 1298 sal_Int32 nMaxWidth = static_cast<sal_Int32>(fabs(aEnd.getX()-aStart.getX())); 1299 1300 sal_Int32 nTotalAvailable = nMaxHeight; 1301 sal_Int32 nSingleNeeded = m_nMaximumTextHeightSoFar; 1302 1303 //for horizontal axis: 1304 if( (m_nDimensionIndex == 0 && !m_aAxisProperties.m_bSwapXAndY) 1305 || (m_nDimensionIndex == 1 && m_aAxisProperties.m_bSwapXAndY) ) 1306 { 1307 nTotalAvailable = nMaxWidth; 1308 nSingleNeeded = m_nMaximumTextWidthSoFar; 1309 } 1310 1311 if( nSingleNeeded>0 ) 1312 nRet = nTotalAvailable/nSingleNeeded; 1313 1314 return nRet; 1315 } 1316 1317 void VCartesianAxis::doStaggeringOfLabels( const AxisLabelProperties& rAxisLabelProperties, TickFactory_2D* pTickFactory2D ) 1318 { 1319 if( !pTickFactory2D ) 1320 return; 1321 1322 if( isComplexCategoryAxis() ) 1323 { 1324 sal_Int32 nTextLevelCount = getTextLevelCount(); 1325 B2DVector aCummulatedLabelsDistance(0,0); 1326 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) 1327 { 1328 ::std::auto_ptr< TickIter > apTickIter = createLabelTickIterator( nTextLevel ); 1329 if(apTickIter.get()) 1330 { 1331 double fRotationAngleDegree = m_aAxisLabelProperties.fRotationAngleDegree; 1332 if( nTextLevel>0 ) 1333 { 1334 lcl_shiftLables( *apTickIter.get(), aCummulatedLabelsDistance ); 1335 fRotationAngleDegree = 0.0; 1336 } 1337 aCummulatedLabelsDistance += lcl_getLabelsDistance( *apTickIter.get() 1338 , pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties ) 1339 , fRotationAngleDegree ); 1340 } 1341 } 1342 } 1343 else if( rAxisLabelProperties.getIsStaggered() ) 1344 { 1345 if( !m_aAllTickInfos.empty() ) 1346 { 1347 LabelIterator aInnerIter( m_aAllTickInfos[0], rAxisLabelProperties.eStaggering, true ); 1348 LabelIterator aOuterIter( m_aAllTickInfos[0], rAxisLabelProperties.eStaggering, false ); 1349 1350 lcl_shiftLables( aOuterIter 1351 , lcl_getLabelsDistance( aInnerIter 1352 , pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties ), 0.0 ) ); 1353 } 1354 } 1355 } 1356 1357 void VCartesianAxis::createLabels() 1358 { 1359 if( !prepareShapeCreation() ) 1360 return; 1361 1362 //----------------------------------------- 1363 //create labels 1364 if( m_aAxisProperties.m_bDisplayLabels ) 1365 { 1366 std::auto_ptr< TickFactory_2D > apTickFactory2D( this->createTickFactory2D() ); 1367 TickFactory_2D* pTickFactory2D = apTickFactory2D.get(); 1368 if( !pTickFactory2D ) 1369 return; 1370 1371 //----------------------------------------- 1372 //get the transformed screen values for all tickmarks in aAllTickInfos 1373 pTickFactory2D->updateScreenValues( m_aAllTickInfos ); 1374 //----------------------------------------- 1375 //'hide' tickmarks with identical screen values in aAllTickInfos 1376 hideIdenticalScreenValues( m_aAllTickInfos ); 1377 1378 removeTextShapesFromTicks(); 1379 1380 //create tick mark text shapes 1381 sal_Int32 nTextLevelCount = getTextLevelCount(); 1382 sal_Int32 nScreenDistanceBetweenTicks = -1; 1383 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) 1384 { 1385 ::std::auto_ptr< TickIter > apTickIter = createLabelTickIterator( nTextLevel ); 1386 if(apTickIter.get()) 1387 { 1388 if(nTextLevel==0) 1389 { 1390 nScreenDistanceBetweenTicks = TickFactory_2D::getTickScreenDistance( *apTickIter.get() ); 1391 if( nTextLevelCount>1 ) 1392 nScreenDistanceBetweenTicks*=2; //the above used tick iter does contain also the sub ticks -> thus the given distance is only the half 1393 } 1394 1395 AxisLabelProperties aComplexProps(m_aAxisLabelProperties); 1396 if( m_aAxisProperties.m_bComplexCategories ) 1397 { 1398 if( nTextLevel==0 ) 1399 { 1400 aComplexProps.bLineBreakAllowed = true; 1401 aComplexProps.bOverlapAllowed = !::rtl::math::approxEqual( aComplexProps.fRotationAngleDegree, 0.0 ); 1402 } 1403 else 1404 { 1405 aComplexProps.bOverlapAllowed = true; 1406 aComplexProps.bRhythmIsFix = true; 1407 aComplexProps.nRhythm = 1; 1408 aComplexProps.fRotationAngleDegree = 0.0; 1409 } 1410 } 1411 AxisLabelProperties& rAxisLabelProperties = m_aAxisProperties.m_bComplexCategories ? aComplexProps : m_aAxisLabelProperties; 1412 while( !createTextShapes( m_xTextTarget, *apTickIter.get(), rAxisLabelProperties, pTickFactory2D, nScreenDistanceBetweenTicks ) ) 1413 { 1414 }; 1415 } 1416 } 1417 doStaggeringOfLabels( m_aAxisLabelProperties, pTickFactory2D ); 1418 } 1419 } 1420 1421 void VCartesianAxis::createMaximumLabels() 1422 { 1423 TrueGuard aRecordMaximumTextSize(m_bRecordMaximumTextSize); 1424 1425 if( !prepareShapeCreation() ) 1426 return; 1427 1428 //----------------------------------------- 1429 //create labels 1430 if( m_aAxisProperties.m_bDisplayLabels ) 1431 { 1432 std::auto_ptr< TickFactory_2D > apTickFactory2D( this->createTickFactory2D() ); 1433 TickFactory_2D* pTickFactory2D = apTickFactory2D.get(); 1434 if( !pTickFactory2D ) 1435 return; 1436 1437 //----------------------------------------- 1438 //get the transformed screen values for all tickmarks in aAllTickInfos 1439 pTickFactory2D->updateScreenValues( m_aAllTickInfos ); 1440 1441 //create tick mark text shapes 1442 //@todo: iterate through all tick depth wich should be labeled 1443 1444 AxisLabelProperties aAxisLabelProperties( m_aAxisLabelProperties ); 1445 if( isAutoStaggeringOfLabelsAllowed( aAxisLabelProperties, pTickFactory2D->isHorizontalAxis(), pTickFactory2D->isVerticalAxis() ) ) 1446 aAxisLabelProperties.eStaggering = STAGGER_EVEN; 1447 aAxisLabelProperties.bOverlapAllowed = true; 1448 aAxisLabelProperties.bLineBreakAllowed = false; 1449 sal_Int32 nTextLevelCount = getTextLevelCount(); 1450 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) 1451 { 1452 ::std::auto_ptr< TickIter > apTickIter = createMaximumLabelTickIterator( nTextLevel ); 1453 if(apTickIter.get()) 1454 { 1455 while( !createTextShapes( m_xTextTarget, *apTickIter.get(), aAxisLabelProperties, pTickFactory2D, -1 ) ) 1456 { 1457 }; 1458 } 1459 } 1460 doStaggeringOfLabels( aAxisLabelProperties, pTickFactory2D ); 1461 } 1462 } 1463 1464 void VCartesianAxis::updatePositions() 1465 { 1466 //----------------------------------------- 1467 //update positions of labels 1468 if( m_aAxisProperties.m_bDisplayLabels ) 1469 { 1470 std::auto_ptr< TickFactory_2D > apTickFactory2D( this->createTickFactory2D() ); 1471 TickFactory_2D* pTickFactory2D = apTickFactory2D.get(); 1472 if( !pTickFactory2D ) 1473 return; 1474 1475 //----------------------------------------- 1476 //update positions of all existing text shapes 1477 pTickFactory2D->updateScreenValues( m_aAllTickInfos ); 1478 1479 ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = m_aAllTickInfos.begin(); 1480 const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = m_aAllTickInfos.end(); 1481 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd; aDepthIter++, nDepth++ ) 1482 { 1483 ::std::vector< TickInfo >::iterator aTickIter = aDepthIter->begin(); 1484 const ::std::vector< TickInfo >::const_iterator aTickEnd = aDepthIter->end(); 1485 for( ; aTickIter != aTickEnd; aTickIter++ ) 1486 { 1487 TickInfo& rTickInfo = (*aTickIter); 1488 Reference< drawing::XShape > xShape2DText( rTickInfo.xTextShape ); 1489 if( xShape2DText.is() ) 1490 { 1491 B2DVector aTextToTickDistance( pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, true ) ); 1492 B2DVector aTickScreenPos2D( rTickInfo.aTickScreenPosition ); 1493 aTickScreenPos2D += aTextToTickDistance; 1494 awt::Point aAnchorScreenPosition2D( 1495 static_cast<sal_Int32>(aTickScreenPos2D.getX()) 1496 ,static_cast<sal_Int32>(aTickScreenPos2D.getY())); 1497 1498 double fRotationAngleDegree = m_aAxisLabelProperties.fRotationAngleDegree; 1499 if( nDepth>0 ) 1500 fRotationAngleDegree = 0.0; 1501 1502 // #i78696# use mathematically correct rotation now 1503 const double fRotationAnglePi(fRotationAngleDegree * (F_PI / -180.0)); 1504 uno::Any aATransformation = ShapeFactory::makeTransformation(aAnchorScreenPosition2D, fRotationAnglePi); 1505 1506 //set new position 1507 uno::Reference< beans::XPropertySet > xProp( xShape2DText, uno::UNO_QUERY ); 1508 if( xProp.is() ) 1509 { 1510 try 1511 { 1512 xProp->setPropertyValue( C2U( "Transformation" ), aATransformation ); 1513 } 1514 catch( uno::Exception& e ) 1515 { 1516 ASSERT_EXCEPTION( e ); 1517 } 1518 } 1519 1520 //correctPositionForRotation 1521 LabelPositionHelper::correctPositionForRotation( xShape2DText 1522 , m_aAxisProperties.m_aLabelAlignment, fRotationAngleDegree, m_aAxisProperties.m_bComplexCategories ); 1523 } 1524 } 1525 } 1526 1527 doStaggeringOfLabels( m_aAxisLabelProperties, pTickFactory2D ); 1528 } 1529 } 1530 1531 void VCartesianAxis::createTickMarkLineShapes( ::std::vector< TickInfo >& rTickInfos, const TickmarkProperties& rTickmarkProperties, TickFactory_2D& rTickFactory2D, bool bOnlyAtLabels ) 1532 { 1533 sal_Int32 nPointCount = rTickInfos.size(); 1534 drawing::PointSequenceSequence aPoints(2*nPointCount); 1535 1536 ::std::vector< TickInfo >::const_iterator aTickIter = rTickInfos.begin(); 1537 const ::std::vector< TickInfo >::const_iterator aTickEnd = rTickInfos.end(); 1538 sal_Int32 nN = 0; 1539 for( ; aTickIter != aTickEnd; aTickIter++ ) 1540 { 1541 if( !(*aTickIter).bPaintIt ) 1542 continue; 1543 1544 bool bTicksAtLabels = ( m_aAxisProperties.m_eTickmarkPos != ::com::sun::star::chart::ChartAxisMarkPosition_AT_AXIS ); 1545 double fInnerDirectionSign = m_aAxisProperties.m_fInnerDirectionSign; 1546 if( bTicksAtLabels && m_aAxisProperties.m_eLabelPos == ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END ) 1547 fInnerDirectionSign *= -1.0; 1548 bTicksAtLabels = bTicksAtLabels || bOnlyAtLabels; 1549 //add ticks at labels: 1550 rTickFactory2D.addPointSequenceForTickLine( aPoints, nN++, (*aTickIter).fScaledTickValue 1551 , fInnerDirectionSign , rTickmarkProperties, bTicksAtLabels ); 1552 //add ticks at axis (without lables): 1553 if( !bOnlyAtLabels && m_aAxisProperties.m_eTickmarkPos == ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS_AND_AXIS ) 1554 rTickFactory2D.addPointSequenceForTickLine( aPoints, nN++, (*aTickIter).fScaledTickValue 1555 , m_aAxisProperties.m_fInnerDirectionSign, rTickmarkProperties, !bTicksAtLabels ); 1556 } 1557 aPoints.realloc(nN); 1558 m_pShapeFactory->createLine2D( m_xGroupShape_Shapes, aPoints 1559 , &rTickmarkProperties.aLineProperties ); 1560 } 1561 1562 void VCartesianAxis::createShapes() 1563 { 1564 if( !prepareShapeCreation() ) 1565 return; 1566 1567 std::auto_ptr< TickFactory_2D > apTickFactory2D( this->createTickFactory2D() ); 1568 TickFactory_2D* pTickFactory2D = apTickFactory2D.get(); 1569 if( !pTickFactory2D ) 1570 return; 1571 1572 //----------------------------------------- 1573 //create line shapes 1574 if(2==m_nDimension) 1575 { 1576 //----------------------------------------- 1577 //create extra long ticks to separate complex categories (create them only there where the labels are) 1578 if( isComplexCategoryAxis() ) 1579 { 1580 ::std::vector< ::std::vector< TickInfo > > aComplexTickInfos; 1581 createAllTickInfosFromComplexCategories( aComplexTickInfos, true ); 1582 pTickFactory2D->updateScreenValues( aComplexTickInfos ); 1583 hideIdenticalScreenValues( aComplexTickInfos ); 1584 1585 ::std::vector<TickmarkProperties> aTickmarkPropertiesList; 1586 static bool bIncludeSpaceBetweenTickAndText = false; 1587 sal_Int32 nOffset = static_cast<sal_Int32>(pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, false, bIncludeSpaceBetweenTickAndText ).getLength()); 1588 sal_Int32 nTextLevelCount = getTextLevelCount(); 1589 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) 1590 { 1591 ::std::auto_ptr< TickIter > apTickIter = createLabelTickIterator( nTextLevel ); 1592 if( apTickIter.get() ) 1593 { 1594 double fRotationAngleDegree = m_aAxisLabelProperties.fRotationAngleDegree; 1595 if( nTextLevel>0 ) 1596 fRotationAngleDegree = 0.0; 1597 B2DVector aLabelsDistance( lcl_getLabelsDistance( *apTickIter.get(), pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, false ), fRotationAngleDegree ) ); 1598 sal_Int32 nCurrentLength = static_cast<sal_Int32>(aLabelsDistance.getLength()); 1599 aTickmarkPropertiesList.push_back( m_aAxisProperties.makeTickmarkPropertiesForComplexCategories( nOffset + nCurrentLength, 0, nTextLevel ) ); 1600 nOffset += nCurrentLength; 1601 } 1602 } 1603 1604 sal_Int32 nTickmarkPropertiesCount = aTickmarkPropertiesList.size(); 1605 ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = aComplexTickInfos.begin(); 1606 const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = aComplexTickInfos.end(); 1607 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd && nDepth < nTickmarkPropertiesCount; aDepthIter++, nDepth++ ) 1608 { 1609 if(nDepth==0 && !m_aAxisProperties.m_nMajorTickmarks) 1610 continue; 1611 createTickMarkLineShapes( *aDepthIter, aTickmarkPropertiesList[nDepth], *pTickFactory2D, true /*bOnlyAtLabels*/ ); 1612 } 1613 } 1614 //----------------------------------------- 1615 //create normal ticks for major and minor intervals 1616 { 1617 ::std::vector< ::std::vector< TickInfo > > aUnshiftedTickInfos; 1618 if( m_aScale.ShiftedCategoryPosition )// if ShiftedCategoryPosition==true the tickmarks in m_aAllTickInfos are shifted 1619 { 1620 pTickFactory2D->getAllTicks( aUnshiftedTickInfos ); 1621 pTickFactory2D->updateScreenValues( aUnshiftedTickInfos ); 1622 hideIdenticalScreenValues( aUnshiftedTickInfos ); 1623 } 1624 ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos = m_aScale.ShiftedCategoryPosition ? aUnshiftedTickInfos : m_aAllTickInfos; 1625 1626 ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = rAllTickInfos.begin(); 1627 const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = rAllTickInfos.end(); 1628 if(aDepthIter == aDepthEnd)//no tickmarks at all 1629 return; 1630 1631 sal_Int32 nTickmarkPropertiesCount = m_aAxisProperties.m_aTickmarkPropertiesList.size(); 1632 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd && nDepth < nTickmarkPropertiesCount; aDepthIter++, nDepth++ ) 1633 createTickMarkLineShapes( *aDepthIter, m_aAxisProperties.m_aTickmarkPropertiesList[nDepth], *pTickFactory2D, false /*bOnlyAtLabels*/ ); 1634 } 1635 //----------------------------------------- 1636 //create axis main lines 1637 //it serves also as the handle shape for the axis selection 1638 { 1639 drawing::PointSequenceSequence aPoints(1); 1640 apTickFactory2D->createPointSequenceForAxisMainLine( aPoints ); 1641 Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( 1642 m_xGroupShape_Shapes, aPoints 1643 , &m_aAxisProperties.m_aLineProperties ); 1644 //because of this name this line will be used for marking the axis 1645 m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); 1646 } 1647 //----------------------------------------- 1648 //create an additional line at NULL 1649 if( !AxisHelper::isAxisPositioningEnabled() ) 1650 { 1651 double fExtraLineCrossesOtherAxis; 1652 if( getLogicValueWhereExtraLineCrossesOtherAxis(fExtraLineCrossesOtherAxis) ) 1653 { 1654 B2DVector aStart, aEnd; 1655 this->get2DAxisMainLine( aStart, aEnd, fExtraLineCrossesOtherAxis ); 1656 drawing::PointSequenceSequence aPoints( lcl_makePointSequence(aStart,aEnd) ); 1657 Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( 1658 m_xGroupShape_Shapes, aPoints, &m_aAxisProperties.m_aLineProperties ); 1659 } 1660 } 1661 } 1662 1663 //createLabels(); 1664 } 1665 1666 //............................................................................. 1667 } //namespace chart 1668 //............................................................................. 1669