fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / chart2 / source / view / axes / VCartesianAxis.cxx
blobc8ae9e095be7d90f6077299c8fdcf12d34aed1e9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "VCartesianAxis.hxx"
21 #include "PlottingPositionHelper.hxx"
22 #include "AbstractShapeFactory.hxx"
23 #include "CommonConverters.hxx"
24 #include "macros.hxx"
25 #include "ViewDefines.hxx"
26 #include "PropertyMapper.hxx"
27 #include "NumberFormatterWrapper.hxx"
28 #include "LabelPositionHelper.hxx"
29 #include "TrueGuard.hxx"
30 #include "BaseGFXHelper.hxx"
31 #include "AxisHelper.hxx"
32 #include "Tickmarks_Equidistant.hxx"
34 #include <rtl/math.hxx>
35 #include <tools/color.hxx>
36 #include <com/sun/star/text/XText.hpp>
37 #include <com/sun/star/text/WritingMode2.hpp>
38 #include <editeng/unoprnms.hxx>
39 #include <svx/unoshape.hxx>
40 #include <svx/unoshtxt.hxx>
42 #include <basegfx/polygon/b2dpolygon.hxx>
43 #include <basegfx/polygon/b2dpolypolygon.hxx>
44 #include <basegfx/polygon/b2dpolygontools.hxx>
45 #include <basegfx/polygon/b2dpolygonclipper.hxx>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <basegfx/numeric/ftools.hxx>
49 #include <algorithm>
50 #include <boost/scoped_ptr.hpp>
52 using namespace ::com::sun::star;
53 using ::com::sun::star::uno::Reference;
54 using ::basegfx::B2DVector;
55 using ::basegfx::B2DPolygon;
56 using ::basegfx::B2DPolyPolygon;
58 namespace chart {
60 VCartesianAxis::VCartesianAxis( const AxisProperties& rAxisProperties
61 , const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier
62 , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount
63 , PlottingPositionHelper* pPosHelper )//takes ownership
64 : VAxisBase( nDimensionIndex, nDimensionCount, rAxisProperties, xNumberFormatsSupplier )
66 if( pPosHelper )
67 m_pPosHelper = pPosHelper;
68 else
69 m_pPosHelper = new PlottingPositionHelper();
72 VCartesianAxis::~VCartesianAxis()
74 delete m_pPosHelper;
75 m_pPosHelper = NULL;
78 Reference< drawing::XShape > createSingleLabel(
79 const Reference< lang::XMultiServiceFactory>& xShapeFactory
80 , const Reference< drawing::XShapes >& xTarget
81 , const awt::Point& rAnchorScreenPosition2D
82 , const OUString& rLabel
83 , const AxisLabelProperties& rAxisLabelProperties
84 , const AxisProperties& rAxisProperties
85 , const tNameSequence& rPropNames
86 , const tAnySequence& rPropValues
89 if(rLabel.isEmpty())
90 return 0;
92 // #i78696# use mathematically correct rotation now
93 const double fRotationAnglePi(rAxisLabelProperties.fRotationAngleDegree * (F_PI / -180.0));
94 uno::Any aATransformation = AbstractShapeFactory::makeTransformation( rAnchorScreenPosition2D, fRotationAnglePi );
95 OUString aLabel = AbstractShapeFactory::getStackedString( rLabel, rAxisLabelProperties.bStackCharacters );
97 Reference< drawing::XShape > xShape2DText = AbstractShapeFactory::getOrCreateShapeFactory(xShapeFactory)
98 ->createText( xTarget, aLabel, rPropNames, rPropValues, aATransformation );
100 LabelPositionHelper::correctPositionForRotation( xShape2DText
101 , rAxisProperties.maLabelAlignment.meAlignment, rAxisLabelProperties.fRotationAngleDegree, rAxisProperties.m_bComplexCategories );
103 return xShape2DText;
106 bool lcl_doesShapeOverlapWithTickmark( const Reference< drawing::XShape >& xShape
107 , double fRotationAngleDegree
108 , const basegfx::B2DVector& rTickScreenPosition
109 , bool bIsHorizontalAxis, bool bIsVerticalAxis )
111 if(!xShape.is())
112 return false;
114 ::basegfx::B2IRectangle aShapeRect = BaseGFXHelper::makeRectangle(xShape->getPosition(),AbstractShapeFactory::getSizeAfterRotation( xShape, fRotationAngleDegree ));
116 if( bIsVerticalAxis )
118 return ( (rTickScreenPosition.getY() >= aShapeRect.getMinY())
119 && (rTickScreenPosition.getY() <= aShapeRect.getMaxY()) );
121 if( bIsHorizontalAxis )
123 return ( (rTickScreenPosition.getX() >= aShapeRect.getMinX())
124 && (rTickScreenPosition.getX() <= aShapeRect.getMaxX()) );
127 basegfx::B2IVector aPosition(
128 static_cast<sal_Int32>( rTickScreenPosition.getX() )
129 , static_cast<sal_Int32>( rTickScreenPosition.getY() ) );
130 return aShapeRect.isInside(aPosition);
133 void lcl_getRotatedPolygon( B2DPolygon &aPoly, const ::basegfx::B2DRectangle &aRect, const awt::Point &aPos, const double fRotationAngleDegree )
135 aPoly = basegfx::tools::createPolygonFromRect( aRect );
137 // For rotating the rectangle we use the opposite angle,
138 // since `B2DHomMatrix` class used for
139 // representing the transformation, performs rotations in the positive
140 // direction (from the X axis to the Y axis). However since the coordinate
141 // system used by the chart has the Y-axis pointing downward, a rotation in
142 // the positive direction means a clockwise rotation. On the contrary text
143 // labels are rotated counterclockwise.
144 // The rotation is performed around the top-left vertex of the rectangle
145 // which is then moved to its final position by using the top-left
146 // vertex of the text label bounding box (aPos) as the translation vector.
147 ::basegfx::B2DHomMatrix aMatrix;
148 aMatrix.rotate( -fRotationAngleDegree*M_PI/180.0 );
149 aMatrix.translate( aPos.X, aPos.Y);
150 aPoly.transform( aMatrix );
153 bool doesOverlap( const Reference< drawing::XShape >& xShape1
154 , const Reference< drawing::XShape >& xShape2
155 , double fRotationAngleDegree )
157 if( !xShape1.is() || !xShape2.is() )
158 return false;
160 ::basegfx::B2DRectangle aRect1( BaseGFXHelper::makeRectangle( awt::Point(0,0), xShape1->getSize()));
161 ::basegfx::B2DRectangle aRect2( BaseGFXHelper::makeRectangle( awt::Point(0,0), xShape2->getSize()));
163 B2DPolygon aPoly1;
164 B2DPolygon aPoly2;
165 lcl_getRotatedPolygon( aPoly1, aRect1, xShape1->getPosition(), fRotationAngleDegree );
166 lcl_getRotatedPolygon( aPoly2, aRect2, xShape2->getPosition(), fRotationAngleDegree );
168 B2DPolyPolygon aPolyPoly1, aPolyPoly2;
169 aPolyPoly1.append( aPoly1 );
170 aPolyPoly2.append( aPoly2 );
171 B2DPolyPolygon overlapPoly = ::basegfx::tools::clipPolyPolygonOnPolyPolygon( aPolyPoly1, aPolyPoly2, true, false );
173 return (overlapPoly.count() > 0);
176 void removeShapesAtWrongRhythm( TickIter& rIter
177 , sal_Int32 nCorrectRhythm
178 , sal_Int32 nMaxTickToCheck
179 , const Reference< drawing::XShapes >& xTarget )
181 sal_Int32 nTick = 0;
182 for( TickInfo* pTickInfo = rIter.firstInfo()
183 ; pTickInfo && nTick <= nMaxTickToCheck
184 ; pTickInfo = rIter.nextInfo(), nTick++ )
186 //remove labels which does not fit into the rhythm
187 if( nTick%nCorrectRhythm != 0)
189 if(pTickInfo->xTextShape.is())
191 xTarget->remove(pTickInfo->xTextShape);
192 pTickInfo->xTextShape = NULL;
199 * If the labels are staggered and bInnerLine is true we iterate through
200 * only those labels that are closer to the diagram.
202 * If the labels are staggered and bInnerLine is false we iterate through
203 * only those that are farther from the diagram.
205 * If the labels are not staggered we iterate through all labels.
207 class LabelIterator : public TickIter
209 public:
210 LabelIterator( TickInfoArrayType& rTickInfoVector
211 , const AxisLabelStaggering eAxisLabelStaggering
212 , bool bInnerLine );
214 virtual TickInfo* firstInfo() SAL_OVERRIDE;
215 virtual TickInfo* nextInfo() SAL_OVERRIDE;
217 private: //member
218 PureTickIter m_aPureTickIter;
219 const AxisLabelStaggering m_eAxisLabelStaggering;
220 bool m_bInnerLine;
223 LabelIterator::LabelIterator( TickInfoArrayType& rTickInfoVector
224 , const AxisLabelStaggering eAxisLabelStaggering
225 , bool bInnerLine )
226 : m_aPureTickIter( rTickInfoVector )
227 , m_eAxisLabelStaggering(eAxisLabelStaggering)
228 , m_bInnerLine(bInnerLine)
232 TickInfo* LabelIterator::firstInfo()
234 TickInfo* pTickInfo = m_aPureTickIter.firstInfo();
235 while( pTickInfo && !pTickInfo->xTextShape.is() )
236 pTickInfo = m_aPureTickIter.nextInfo();
237 if(!pTickInfo)
238 return NULL;
239 if( (STAGGER_EVEN==m_eAxisLabelStaggering && m_bInnerLine)
241 (STAGGER_ODD==m_eAxisLabelStaggering && !m_bInnerLine)
244 //skip first label
246 pTickInfo = m_aPureTickIter.nextInfo();
247 while( pTickInfo && !pTickInfo->xTextShape.is() );
249 if(!pTickInfo)
250 return NULL;
251 return pTickInfo;
254 TickInfo* LabelIterator::nextInfo()
256 TickInfo* pTickInfo = NULL;
257 //get next label
259 pTickInfo = m_aPureTickIter.nextInfo();
260 while( pTickInfo && !pTickInfo->xTextShape.is() );
262 if( STAGGER_EVEN==m_eAxisLabelStaggering
263 || STAGGER_ODD==m_eAxisLabelStaggering )
265 //skip one label
267 pTickInfo = m_aPureTickIter.nextInfo();
268 while( pTickInfo && !pTickInfo->xTextShape.is() );
270 return pTickInfo;
273 B2DVector lcl_getLabelsDistance( TickIter& rIter, const B2DVector& rDistanceTickToText, double fRotationAngleDegree )
275 //calculates the height or width of a line of labels
276 //thus a following line of labels can be shifted for that distance
278 B2DVector aRet(0,0);
280 sal_Int32 nDistanceTickToText = static_cast<sal_Int32>( rDistanceTickToText.getLength() );
281 if( nDistanceTickToText==0.0)
282 return aRet;
284 B2DVector aStaggerDirection(rDistanceTickToText);
285 aStaggerDirection.normalize();
287 sal_Int32 nDistance=0;
288 Reference< drawing::XShape > xShape2DText(NULL);
289 for( TickInfo* pTickInfo = rIter.firstInfo()
290 ; pTickInfo
291 ; pTickInfo = rIter.nextInfo() )
293 xShape2DText = pTickInfo->xTextShape;
294 if( xShape2DText.is() )
296 awt::Size aSize = AbstractShapeFactory::getSizeAfterRotation( xShape2DText, fRotationAngleDegree );
297 if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY()))
298 nDistance = ::std::max(nDistance,aSize.Width);
299 else
300 nDistance = ::std::max(nDistance,aSize.Height);
304 aRet = aStaggerDirection*nDistance;
306 //add extra distance for vertical distance
307 if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY()))
308 aRet += rDistanceTickToText;
310 return aRet;
313 void lcl_shiftLabels( TickIter& rIter, const B2DVector& rStaggerDistance )
315 if(rStaggerDistance.getLength()==0.0)
316 return;
317 Reference< drawing::XShape > xShape2DText(NULL);
318 for( TickInfo* pTickInfo = rIter.firstInfo()
319 ; pTickInfo
320 ; pTickInfo = rIter.nextInfo() )
322 xShape2DText = pTickInfo->xTextShape;
323 if( xShape2DText.is() )
325 awt::Point aPos = xShape2DText->getPosition();
326 aPos.X += static_cast<sal_Int32>(rStaggerDistance.getX());
327 aPos.Y += static_cast<sal_Int32>(rStaggerDistance.getY());
328 xShape2DText->setPosition( aPos );
333 bool lcl_hasWordBreak( const Reference<drawing::XShape>& xShape )
335 if (!xShape.is())
336 return false;
338 SvxShape* pShape = SvxShape::getImplementation(xShape);
339 SvxShapeText* pShapeText = dynamic_cast<SvxShapeText*>(pShape);
340 if (!pShapeText)
341 return false;
343 SvxTextEditSource* pTextEditSource = dynamic_cast<SvxTextEditSource*>(pShapeText->GetEditSource());
344 if (!pTextEditSource)
345 return false;
347 pTextEditSource->UpdateOutliner();
348 SvxTextForwarder* pTextForwarder = pTextEditSource->GetTextForwarder();
349 if (!pTextForwarder)
350 return false;
352 sal_Int32 nParaCount = pTextForwarder->GetParagraphCount();
353 for ( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
355 sal_Int32 nLineCount = pTextForwarder->GetLineCount( nPara );
356 for ( sal_Int32 nLine = 0; nLine < nLineCount; ++nLine )
358 sal_Int32 nLineStart = 0;
359 sal_Int32 nLineEnd = 0;
360 pTextForwarder->GetLineBoundaries( nLineStart, nLineEnd, nPara, nLine );
361 assert(nLineStart >= 0);
362 sal_Int32 nWordStart = 0;
363 sal_Int32 nWordEnd = 0;
364 if ( pTextForwarder->GetWordIndices( nPara, nLineStart, nWordStart, nWordEnd ) &&
365 ( nWordStart != nLineStart ) )
367 return true;
372 return false;
375 OUString getTextLabelString(
376 const FixedNumberFormatter& rFixedNumberFormatter, const uno::Sequence<OUString>* pCategories,
377 const TickInfo* pTickInfo, bool bComplexCat, sal_Int32& rExtraColor, bool& rHasExtraColor )
379 if (pCategories)
381 // This is a normal category axis. Get the label string from the
382 // label string array.
383 sal_Int32 nIndex = static_cast<sal_Int32>(pTickInfo->getUnscaledTickValue()) - 1; //first category (index 0) matches with real number 1.0
384 if( nIndex>=0 && nIndex<pCategories->getLength() )
385 return (*pCategories)[nIndex];
387 return OUString();
389 else if (bComplexCat)
391 // This is a complex category axis. The label is stored in the tick.
392 return pTickInfo->aText;
395 // This is a numeric axis. Format the original tick value per number format.
396 return rFixedNumberFormatter.getFormattedString(pTickInfo->getUnscaledTickValue(), rExtraColor, rHasExtraColor);
399 void getAxisLabelProperties(
400 tNameSequence& rPropNames, tAnySequence& rPropValues, const AxisProperties& rAxisProp,
401 const AxisLabelProperties& rAxisLabelProp,
402 sal_Int32 nLimitedSpaceForText, bool bLimitedHeight )
404 Reference<beans::XPropertySet> xProps(rAxisProp.m_xAxisModel, uno::UNO_QUERY);
406 PropertyMapper::getTextLabelMultiPropertyLists(
407 xProps, rPropNames, rPropValues, false, nLimitedSpaceForText, bLimitedHeight);
409 LabelPositionHelper::doDynamicFontResize(
410 rPropValues, rPropNames, xProps, rAxisLabelProp.m_aFontReferenceSize);
412 LabelPositionHelper::changeTextAdjustment(
413 rPropValues, rPropNames, rAxisProp.maLabelAlignment.meAlignment);
417 * Iterate through only 3 ticks including the one that has the longest text
418 * length. When the first tick has the longest text, it iterates through
419 * the first 3 ticks. Otherwise it iterates through 3 ticks such that the
420 * 2nd tick is the one with the longest text.
422 class MaxLabelTickIter : public TickIter
424 public:
425 MaxLabelTickIter( TickInfoArrayType& rTickInfoVector, size_t nLongestLabelIndex );
426 virtual ~MaxLabelTickIter();
428 virtual TickInfo* firstInfo() SAL_OVERRIDE;
429 virtual TickInfo* nextInfo() SAL_OVERRIDE;
431 private:
432 TickInfoArrayType& m_rTickInfoVector;
433 std::vector<size_t> m_aValidIndices;
434 size_t m_nCurrentIndex;
437 MaxLabelTickIter::MaxLabelTickIter(
438 TickInfoArrayType& rTickInfoVector, size_t nLongestLabelIndex ) :
439 m_rTickInfoVector(rTickInfoVector), m_nCurrentIndex(0)
441 assert(!rTickInfoVector.empty()); // should be checked by the caller.
442 assert(nLongestLabelIndex < rTickInfoVector.size());
444 size_t nMaxIndex = m_rTickInfoVector.size()-1;
445 if (nLongestLabelIndex >= nMaxIndex-1)
446 nLongestLabelIndex = 0;
448 if (nLongestLabelIndex > 0)
449 m_aValidIndices.push_back(nLongestLabelIndex-1);
451 m_aValidIndices.push_back(nLongestLabelIndex);
453 while (m_aValidIndices.size() < 3)
455 ++nLongestLabelIndex;
456 if (nLongestLabelIndex > nMaxIndex)
457 break;
459 m_aValidIndices.push_back(nLongestLabelIndex);
463 MaxLabelTickIter::~MaxLabelTickIter()
467 TickInfo* MaxLabelTickIter::firstInfo()
469 m_nCurrentIndex = 0;
470 if (m_nCurrentIndex < m_aValidIndices.size())
471 return &m_rTickInfoVector[m_aValidIndices[m_nCurrentIndex]];
472 return 0;
475 TickInfo* MaxLabelTickIter::nextInfo()
477 m_nCurrentIndex++;
478 if (m_nCurrentIndex < m_aValidIndices.size())
479 return &m_rTickInfoVector[m_aValidIndices[m_nCurrentIndex]];
480 return 0;
483 bool VCartesianAxis::isBreakOfLabelsAllowed(
484 const AxisLabelProperties& rAxisLabelProperties, bool bIsHorizontalAxis ) const
486 if( m_aTextLabels.getLength() > 100 )
487 return false;
488 if( !rAxisLabelProperties.bLineBreakAllowed )
489 return false;
490 if( rAxisLabelProperties.bStackCharacters )
491 return false;
492 //no break for value axis
493 if( !m_bUseTextLabels )
494 return false;
495 if( !::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) )
496 return false;
497 //break only for horizontal axis
498 return bIsHorizontalAxis;
501 bool VCartesianAxis::isAutoStaggeringOfLabelsAllowed(
502 const AxisLabelProperties& rAxisLabelProperties, bool bIsHorizontalAxis, bool bIsVerticalAxis )
504 if( rAxisLabelProperties.eStaggering != STAGGER_AUTO )
505 return false;
506 if( rAxisLabelProperties.bOverlapAllowed )
507 return false;
508 if( rAxisLabelProperties.bLineBreakAllowed ) //auto line break or auto staggering, doing both automatisms they may conflict...
509 return false;
510 if( !::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) )
511 return false;
512 //automatic staggering only for horizontal axis with horizontal text
513 //or vertical axis with vertical text
514 if( bIsHorizontalAxis )
515 return !rAxisLabelProperties.bStackCharacters;
516 if( bIsVerticalAxis )
517 return rAxisLabelProperties.bStackCharacters;
518 return false;
521 void VCartesianAxis::createAllTickInfosFromComplexCategories( TickInfoArraysType& rAllTickInfos, bool bShiftedPosition )
523 //no minor tickmarks will be generated!
524 //order is: inner labels first , outer labels last (that is different to all other TickIter cases)
525 if(!bShiftedPosition)
527 rAllTickInfos.clear();
528 sal_Int32 nLevel=0;
529 sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount();
530 for( ; nLevel<nLevelCount; nLevel++ )
532 TickInfoArrayType aTickInfoVector;
533 const std::vector<ComplexCategory>* pComplexCategories =
534 m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel(nLevel);
536 if (!pComplexCategories)
537 continue;
539 sal_Int32 nCatIndex = 0;
540 std::vector<ComplexCategory>::const_iterator aIt = pComplexCategories->begin();
541 std::vector<ComplexCategory>::const_iterator aEnd = pComplexCategories->end();
543 for(;aIt!=aEnd;++aIt)
545 TickInfo aTickInfo(0);
546 ComplexCategory aCat(*aIt);
547 sal_Int32 nCount = aCat.Count;
548 if( nCatIndex + 1.0 + nCount >= m_aScale.Maximum )
550 nCount = static_cast<sal_Int32>(m_aScale.Maximum - 1.0 - nCatIndex);
551 if( nCount <= 0 )
552 nCount = 1;
554 aTickInfo.fScaledTickValue = nCatIndex + 1.0 + nCount/2.0;
555 aTickInfo.nFactorForLimitedTextWidth = nCount;
556 aTickInfo.aText = aCat.Text;
557 aTickInfoVector.push_back(aTickInfo);
558 nCatIndex += nCount;
559 if( nCatIndex + 1.0 >= m_aScale.Maximum )
560 break;
562 rAllTickInfos.push_back(aTickInfoVector);
565 else //bShiftedPosition==false
567 rAllTickInfos.clear();
568 sal_Int32 nLevel=0;
569 sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount();
570 for( ; nLevel<nLevelCount; nLevel++ )
572 TickInfoArrayType aTickInfoVector;
573 const std::vector<ComplexCategory>* pComplexCategories =
574 m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel(nLevel);
575 sal_Int32 nCatIndex = 0;
576 if (pComplexCategories)
578 std::vector<ComplexCategory>::const_iterator aIt = pComplexCategories->begin();
579 std::vector<ComplexCategory>::const_iterator aEnd = pComplexCategories->end();
580 for(;aIt!=aEnd;++aIt)
582 TickInfo aTickInfo(0);
583 ComplexCategory aCat(*aIt);
584 aTickInfo.fScaledTickValue = nCatIndex + 1.0;
585 aTickInfoVector.push_back(aTickInfo);
586 nCatIndex += aCat.Count;
587 if( nCatIndex + 1.0 > m_aScale.Maximum )
588 break;
592 //fill up with single ticks until maximum scale
593 while( nCatIndex + 1.0 < m_aScale.Maximum )
595 TickInfo aTickInfo(0);
596 aTickInfo.fScaledTickValue = nCatIndex + 1.0;
597 aTickInfoVector.push_back(aTickInfo);
598 nCatIndex ++;
599 if( nLevel>0 )
600 break;
602 //add an additional tick at the end
604 TickInfo aTickInfo(0);
605 aTickInfo.fScaledTickValue = m_aScale.Maximum;
606 aTickInfoVector.push_back(aTickInfo);
608 rAllTickInfos.push_back(aTickInfoVector);
613 void VCartesianAxis::createAllTickInfos( TickInfoArraysType& rAllTickInfos )
615 if( isComplexCategoryAxis() )
616 createAllTickInfosFromComplexCategories( rAllTickInfos, false );
617 else
618 VAxisBase::createAllTickInfos(rAllTickInfos);
621 TickIter* VCartesianAxis::createLabelTickIterator( sal_Int32 nTextLevel )
623 if( nTextLevel>=0 && nTextLevel < static_cast< sal_Int32 >(m_aAllTickInfos.size()) )
624 return new PureTickIter( m_aAllTickInfos[nTextLevel] );
625 return NULL;
628 TickIter* VCartesianAxis::createMaximumLabelTickIterator( sal_Int32 nTextLevel )
630 if( isComplexCategoryAxis() || isDateAxis() )
632 return createLabelTickIterator( nTextLevel ); //mmmm maybe todo: create less than all texts here
634 else
636 if(nTextLevel==0)
638 if( !m_aAllTickInfos.empty() )
640 size_t nLongestLabelIndex = m_bUseTextLabels ? getIndexOfLongestLabel(m_aTextLabels) : 0;
641 if (nLongestLabelIndex >= m_aAllTickInfos[0].size())
642 return NULL;
644 return new MaxLabelTickIter( m_aAllTickInfos[0], nLongestLabelIndex );
648 return NULL;
651 sal_Int32 VCartesianAxis::getTextLevelCount() const
653 sal_Int32 nTextLevelCount = 1;
654 if( isComplexCategoryAxis() )
655 nTextLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount();
656 return nTextLevelCount;
659 bool VCartesianAxis::createTextShapes(
660 const Reference<drawing::XShapes>& xTarget, TickIter& rTickIter,
661 AxisLabelProperties& rAxisLabelProperties, TickFactory2D* pTickFactory,
662 sal_Int32 nScreenDistanceBetweenTicks )
664 const bool bIsHorizontalAxis = pTickFactory->isHorizontalAxis();
665 const bool bIsVerticalAxis = pTickFactory->isVerticalAxis();
667 if (!isBreakOfLabelsAllowed(rAxisLabelProperties, bIsHorizontalAxis) &&
668 !isAutoStaggeringOfLabelsAllowed(rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis) &&
669 !rAxisLabelProperties.isStaggered())
670 return createTextShapesSimple(xTarget, rTickIter, rAxisLabelProperties, pTickFactory);
672 FixedNumberFormatter aFixedNumberFormatter(
673 m_xNumberFormatsSupplier, rAxisLabelProperties.nNumberFormatKey );
675 bool bIsStaggered = rAxisLabelProperties.isStaggered();
676 B2DVector aTextToTickDistance = pTickFactory->getDistanceAxisTickToText(m_aAxisProperties, true);
677 sal_Int32 nLimitedSpaceForText = -1;
679 if( isBreakOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis ) )
681 nLimitedSpaceForText = nScreenDistanceBetweenTicks;
682 if( bIsStaggered )
683 nLimitedSpaceForText *= 2;
685 if( nLimitedSpaceForText > 0 )
686 { //reduce space for a small amount to have a visible distance between the labels:
687 sal_Int32 nReduce = (nLimitedSpaceForText*5)/100;
688 if(!nReduce)
689 nReduce = 1;
690 nLimitedSpaceForText -= nReduce;
694 // Stores an array of text label strings in case of a normal
695 // (non-complex) category axis.
696 const uno::Sequence<OUString>* pCategories = NULL;
697 if( m_bUseTextLabels && !m_aAxisProperties.m_bComplexCategories )
698 pCategories = &m_aTextLabels;
700 bool bLimitedHeight = fabs(aTextToTickDistance.getX()) > fabs(aTextToTickDistance.getY());
702 //prepare properties for multipropertyset-interface of shape
703 tNameSequence aPropNames;
704 tAnySequence aPropValues;
705 getAxisLabelProperties(aPropNames, aPropValues, m_aAxisProperties, rAxisLabelProperties, nLimitedSpaceForText, bLimitedHeight);
707 uno::Any* pColorAny = PropertyMapper::getValuePointer(aPropValues,aPropNames,"CharColor");
708 sal_Int32 nColor = Color( COL_AUTO ).GetColor();
709 if(pColorAny)
710 *pColorAny >>= nColor;
712 uno::Any* pLimitedSpaceAny = PropertyMapper::getValuePointerForLimitedSpace(aPropValues,aPropNames,bLimitedHeight);
714 const TickInfo* pPreviousVisibleTickInfo = NULL;
715 const TickInfo* pPREPreviousVisibleTickInfo = NULL;
716 const TickInfo* pLastVisibleNeighbourTickInfo = NULL;
717 sal_Int32 nTick = 0;
718 for( TickInfo* pTickInfo = rTickIter.firstInfo()
719 ; pTickInfo
720 ; pTickInfo = rTickIter.nextInfo(), nTick++ )
722 pLastVisibleNeighbourTickInfo = bIsStaggered ?
723 pPREPreviousVisibleTickInfo : pPreviousVisibleTickInfo;
725 //don't create labels which does not fit into the rhythm
726 if( nTick%rAxisLabelProperties.nRhythm != 0 )
727 continue;
729 //don't create labels for invisible ticks
730 if( !pTickInfo->bPaintIt )
731 continue;
733 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed )
735 // Overlapping is not allowed. If the label overlaps with its
736 // neighbering label, try increasing the tick interval (or rhythm
737 // as it's called) and start over.
739 if( lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape
740 , rAxisLabelProperties.fRotationAngleDegree
741 , pTickInfo->aTickScreenPosition
742 , bIsHorizontalAxis, bIsVerticalAxis ) )
744 // This tick overlaps with its neighbor. Try to stagger (if
745 // auto staggering is allowed) to avoid overlapping.
747 bool bOverlapsAfterAutoStagger = true;
748 if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) )
750 bIsStaggered = true;
751 rAxisLabelProperties.eStaggering = STAGGER_EVEN;
752 pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo;
753 if( !pLastVisibleNeighbourTickInfo ||
754 !lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape
755 , rAxisLabelProperties.fRotationAngleDegree
756 , pTickInfo->aTickScreenPosition
757 , bIsHorizontalAxis, bIsVerticalAxis ) )
758 bOverlapsAfterAutoStagger = false;
761 if (bOverlapsAfterAutoStagger)
763 // Still overlaps with its neighbor even after staggering.
764 // Increment the visible tick intervals (if that's
765 // allowed) and start over.
767 if( rAxisLabelProperties.bRhythmIsFix )
768 continue;
769 rAxisLabelProperties.nRhythm++;
770 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget );
771 return false;
776 bool bHasExtraColor=false;
777 sal_Int32 nExtraColor=0;
779 OUString aLabel = getTextLabelString(
780 aFixedNumberFormatter, pCategories, pTickInfo, isComplexCategoryAxis(),
781 nExtraColor, bHasExtraColor);
783 if(pColorAny)
784 *pColorAny = uno::makeAny(bHasExtraColor?nExtraColor:nColor);
785 if(pLimitedSpaceAny)
786 *pLimitedSpaceAny = uno::makeAny(sal_Int32(nLimitedSpaceForText*pTickInfo->nFactorForLimitedTextWidth));
788 B2DVector aTickScreenPos2D = pTickInfo->aTickScreenPosition;
789 aTickScreenPos2D += aTextToTickDistance;
790 awt::Point aAnchorScreenPosition2D(
791 static_cast<sal_Int32>(aTickScreenPos2D.getX())
792 ,static_cast<sal_Int32>(aTickScreenPos2D.getY()));
794 //create single label
795 if(!pTickInfo->xTextShape.is())
796 pTickInfo->xTextShape = createSingleLabel( m_xShapeFactory, xTarget
797 , aAnchorScreenPosition2D, aLabel
798 , rAxisLabelProperties, m_aAxisProperties
799 , aPropNames, aPropValues );
800 if(!pTickInfo->xTextShape.is())
801 continue;
803 recordMaximumTextSize( pTickInfo->xTextShape, rAxisLabelProperties.fRotationAngleDegree );
805 if( nLimitedSpaceForText>0 && !rAxisLabelProperties.bOverlapAllowed
806 && ::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 )
807 && m_aAxisProperties.m_bComplexCategories
808 && lcl_hasWordBreak( pTickInfo->xTextShape ) )
810 // Label has multiple lines and belongs to a complex category
811 // axis. Rotate 90 degrees to try to avoid overlaps.
812 rAxisLabelProperties.fRotationAngleDegree = 90;
813 rAxisLabelProperties.bLineBreakAllowed = false;
814 m_aAxisLabelProperties.fRotationAngleDegree = rAxisLabelProperties.fRotationAngleDegree;
815 removeTextShapesFromTicks();
816 return false;
819 //if NO OVERLAP -> remove overlapping shapes
820 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed )
822 // Check if the label still overlaps with its neighber.
823 if( doesOverlap( pLastVisibleNeighbourTickInfo->xTextShape, pTickInfo->xTextShape, rAxisLabelProperties.fRotationAngleDegree ) )
825 // It overlaps. Check if staggering helps.
826 bool bOverlapsAfterAutoStagger = true;
827 if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) )
829 bIsStaggered = true;
830 rAxisLabelProperties.eStaggering = STAGGER_EVEN;
831 pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo;
832 if( !pLastVisibleNeighbourTickInfo ||
833 !lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape
834 , rAxisLabelProperties.fRotationAngleDegree
835 , pTickInfo->aTickScreenPosition
836 , bIsHorizontalAxis, bIsVerticalAxis ) )
837 bOverlapsAfterAutoStagger = false;
840 if (bOverlapsAfterAutoStagger)
842 // Staggering didn't solve the overlap.
843 if( !rAxisLabelProperties.bOverlapAllowed && ::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) )
845 // Try auto-rotating the labels at 45 degrees and
846 // start over. This rotation angle will be stored for
847 // all future text shape creation runs.
848 // The nRhythm parameter is reset to 1 since the layout
849 // used for text labels is changed.
850 rAxisLabelProperties.autoRotate45();
851 m_aAxisLabelProperties.fRotationAngleDegree = rAxisLabelProperties.fRotationAngleDegree; // Store it for future runs.
852 removeTextShapesFromTicks();
853 rAxisLabelProperties.nRhythm = 1;
854 return false;
857 if( rAxisLabelProperties.bRhythmIsFix )
859 // Tick interval is fixed. We have no choice but to
860 // remove this label.
861 xTarget->remove(pTickInfo->xTextShape);
862 pTickInfo->xTextShape = NULL;
863 continue;
866 // Try incrementing the tick interval and start over.
867 rAxisLabelProperties.nRhythm++;
868 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget );
869 return false;
874 pPREPreviousVisibleTickInfo = pPreviousVisibleTickInfo;
875 pPreviousVisibleTickInfo = pTickInfo;
877 return true;
880 bool VCartesianAxis::createTextShapesSimple(
881 const Reference<drawing::XShapes>& xTarget, TickIter& rTickIter,
882 AxisLabelProperties& rAxisLabelProperties, TickFactory2D* pTickFactory )
884 FixedNumberFormatter aFixedNumberFormatter(
885 m_xNumberFormatsSupplier, rAxisLabelProperties.nNumberFormatKey );
887 const bool bIsHorizontalAxis = pTickFactory->isHorizontalAxis();
888 const bool bIsVerticalAxis = pTickFactory->isVerticalAxis();
889 B2DVector aTextToTickDistance = pTickFactory->getDistanceAxisTickToText(m_aAxisProperties, true);
891 // Stores an array of text label strings in case of a normal
892 // (non-complex) category axis.
893 const uno::Sequence<OUString>* pCategories = NULL;
894 if( m_bUseTextLabels && !m_aAxisProperties.m_bComplexCategories )
895 pCategories = &m_aTextLabels;
897 bool bLimitedHeight = fabs(aTextToTickDistance.getX()) > fabs(aTextToTickDistance.getY());
899 //prepare properties for multipropertyset-interface of shape
900 tNameSequence aPropNames;
901 tAnySequence aPropValues;
902 getAxisLabelProperties(aPropNames, aPropValues, m_aAxisProperties, rAxisLabelProperties, -1, bLimitedHeight);
904 uno::Any* pColorAny = PropertyMapper::getValuePointer(aPropValues,aPropNames,"CharColor");
905 sal_Int32 nColor = Color( COL_AUTO ).GetColor();
906 if(pColorAny)
907 *pColorAny >>= nColor;
909 uno::Any* pLimitedSpaceAny = PropertyMapper::getValuePointerForLimitedSpace(aPropValues,aPropNames,bLimitedHeight);
911 const TickInfo* pPreviousVisibleTickInfo = NULL;
912 const TickInfo* pLastVisibleNeighbourTickInfo = NULL;
913 sal_Int32 nTick = 0;
914 for( TickInfo* pTickInfo = rTickIter.firstInfo()
915 ; pTickInfo
916 ; pTickInfo = rTickIter.nextInfo(), nTick++ )
918 pLastVisibleNeighbourTickInfo = pPreviousVisibleTickInfo;
920 //don't create labels which does not fit into the rhythm
921 if( nTick%rAxisLabelProperties.nRhythm != 0 )
922 continue;
924 //don't create labels for invisible ticks
925 if( !pTickInfo->bPaintIt )
926 continue;
928 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed )
930 // Overlapping is not allowed. If the label overlaps with its
931 // neighbering label, try increasing the tick interval (or rhythm
932 // as it's called) and start over.
934 if( lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape
935 , rAxisLabelProperties.fRotationAngleDegree
936 , pTickInfo->aTickScreenPosition
937 , bIsHorizontalAxis, bIsVerticalAxis ) )
939 // This tick overlaps with its neighbor. Increment the visible
940 // tick intervals (if that's allowed) and start over.
942 if( rAxisLabelProperties.bRhythmIsFix )
943 continue;
944 rAxisLabelProperties.nRhythm++;
945 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget );
946 return false;
950 bool bHasExtraColor=false;
951 sal_Int32 nExtraColor=0;
953 OUString aLabel = getTextLabelString(
954 aFixedNumberFormatter, pCategories, pTickInfo, isComplexCategoryAxis(),
955 nExtraColor, bHasExtraColor);
957 if(pColorAny)
958 *pColorAny = uno::makeAny(bHasExtraColor?nExtraColor:nColor);
959 if(pLimitedSpaceAny)
960 *pLimitedSpaceAny = uno::makeAny(sal_Int32(-1*pTickInfo->nFactorForLimitedTextWidth));
962 B2DVector aTickScreenPos2D = pTickInfo->aTickScreenPosition;
963 aTickScreenPos2D += aTextToTickDistance;
964 awt::Point aAnchorScreenPosition2D(
965 static_cast<sal_Int32>(aTickScreenPos2D.getX())
966 ,static_cast<sal_Int32>(aTickScreenPos2D.getY()));
968 //create single label
969 if(!pTickInfo->xTextShape.is())
970 pTickInfo->xTextShape = createSingleLabel( m_xShapeFactory, xTarget
971 , aAnchorScreenPosition2D, aLabel
972 , rAxisLabelProperties, m_aAxisProperties
973 , aPropNames, aPropValues );
974 if(!pTickInfo->xTextShape.is())
975 continue;
977 recordMaximumTextSize( pTickInfo->xTextShape, rAxisLabelProperties.fRotationAngleDegree );
979 //if NO OVERLAP -> remove overlapping shapes
980 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed )
982 // Check if the label still overlaps with its neighber.
983 if( doesOverlap( pLastVisibleNeighbourTickInfo->xTextShape, pTickInfo->xTextShape, rAxisLabelProperties.fRotationAngleDegree ) )
985 // It overlaps.
986 if( !rAxisLabelProperties.bOverlapAllowed && ::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) )
988 // Try auto-rotating the labels at 45 degrees and
989 // start over. This rotation angle will be stored for
990 // all future text shape creation runs.
991 // The nRhythm parameter is reset to 1 since the layout
992 // used for text labels is changed.
993 rAxisLabelProperties.autoRotate45();
994 m_aAxisLabelProperties.fRotationAngleDegree = rAxisLabelProperties.fRotationAngleDegree; // Store it for future runs.
995 removeTextShapesFromTicks();
996 rAxisLabelProperties.nRhythm = 1;
997 return false;
1000 if( rAxisLabelProperties.bRhythmIsFix )
1002 // Tick interval is fixed. We have no choice but to
1003 // remove this label.
1004 xTarget->remove(pTickInfo->xTextShape);
1005 pTickInfo->xTextShape = NULL;
1006 continue;
1009 // Try incrementing the tick interval and start over.
1010 rAxisLabelProperties.nRhythm++;
1011 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget );
1012 return false;
1016 pPreviousVisibleTickInfo = pTickInfo;
1018 return true;
1021 drawing::PointSequenceSequence lcl_makePointSequence( B2DVector& rStart, B2DVector& rEnd )
1023 drawing::PointSequenceSequence aPoints(1);
1024 aPoints[0].realloc(2);
1025 aPoints[0][0].X = static_cast<sal_Int32>(rStart.getX());
1026 aPoints[0][0].Y = static_cast<sal_Int32>(rStart.getY());
1027 aPoints[0][1].X = static_cast<sal_Int32>(rEnd.getX());
1028 aPoints[0][1].Y = static_cast<sal_Int32>(rEnd.getY());
1029 return aPoints;
1032 double VCartesianAxis::getAxisIntersectionValue() const
1034 if (m_aAxisProperties.m_pfMainLinePositionAtOtherAxis)
1035 return *m_aAxisProperties.m_pfMainLinePositionAtOtherAxis;
1037 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY();
1038 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY();
1040 return (css::chart::ChartAxisPosition_END == m_aAxisProperties.m_eCrossoverType) ? fMax : fMin;
1043 double VCartesianAxis::getLabelLineIntersectionValue() const
1045 if (css::chart::ChartAxisLabelPosition_OUTSIDE_START == m_aAxisProperties.m_eLabelPos)
1046 return (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY();
1048 if (css::chart::ChartAxisLabelPosition_OUTSIDE_END == m_aAxisProperties.m_eLabelPos)
1049 return (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY();
1051 return getAxisIntersectionValue();
1054 double VCartesianAxis::getExtraLineIntersectionValue() const
1056 double fNan;
1057 rtl::math::setNan(&fNan);
1059 if( !m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis )
1060 return fNan;
1062 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY();
1063 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY();
1065 if( *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis <= fMin
1066 || *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis >= fMax )
1067 return fNan;
1069 return *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis;
1072 B2DVector VCartesianAxis::getScreenPosition( double fLogicX, double fLogicY, double fLogicZ ) const
1074 B2DVector aRet(0,0);
1076 if( m_pPosHelper )
1078 drawing::Position3D aScenePos = m_pPosHelper->transformLogicToScene( fLogicX, fLogicY, fLogicZ, true );
1079 if(3==m_nDimension)
1081 if( m_xLogicTarget.is() && m_pPosHelper && m_pShapeFactory )
1083 tPropertyNameMap aDummyPropertyNameMap;
1084 Reference< drawing::XShape > xShape3DAnchor = m_pShapeFactory->createCube( m_xLogicTarget
1085 , aScenePos,drawing::Direction3D(1,1,1), 0, 0, aDummyPropertyNameMap);
1086 awt::Point a2DPos = xShape3DAnchor->getPosition(); //get 2D position from xShape3DAnchor
1087 m_xLogicTarget->remove(xShape3DAnchor);
1088 aRet.setX( a2DPos.X );
1089 aRet.setY( a2DPos.Y );
1091 else
1093 OSL_FAIL("cannot calculate scrren position in VCartesianAxis::getScreenPosition");
1096 else
1098 aRet.setX( aScenePos.PositionX );
1099 aRet.setY( aScenePos.PositionY );
1103 return aRet;
1106 VCartesianAxis::ScreenPosAndLogicPos VCartesianAxis::getScreenPosAndLogicPos( double fLogicX_, double fLogicY_, double fLogicZ_ ) const
1108 ScreenPosAndLogicPos aRet;
1109 aRet.fLogicX = fLogicX_;
1110 aRet.fLogicY = fLogicY_;
1111 aRet.fLogicZ = fLogicZ_;
1112 aRet.aScreenPos = getScreenPosition( fLogicX_, fLogicY_, fLogicZ_ );
1113 return aRet;
1116 typedef ::std::vector< VCartesianAxis::ScreenPosAndLogicPos > tScreenPosAndLogicPosList;
1117 struct lcl_LessXPos : ::std::binary_function< VCartesianAxis::ScreenPosAndLogicPos, VCartesianAxis::ScreenPosAndLogicPos, bool >
1119 inline bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 )
1121 return ( rPos1.aScreenPos.getX() < rPos2.aScreenPos.getX() );
1125 struct lcl_GreaterYPos : ::std::binary_function< VCartesianAxis::ScreenPosAndLogicPos, VCartesianAxis::ScreenPosAndLogicPos, bool >
1127 inline bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 )
1129 return ( rPos1.aScreenPos.getY() > rPos2.aScreenPos.getY() );
1133 void VCartesianAxis::get2DAxisMainLine(
1134 B2DVector& rStart, B2DVector& rEnd, AxisLabelAlignment& rAlignment, double fCrossesOtherAxis ) const
1136 //m_aAxisProperties might get updated and changed here because
1137 // the label alignmant and inner direction sign depends exactly of the choice of the axis line position which is made here in this method
1139 double fMinX = m_pPosHelper->getLogicMinX();
1140 double fMinY = m_pPosHelper->getLogicMinY();
1141 double fMinZ = m_pPosHelper->getLogicMinZ();
1142 double fMaxX = m_pPosHelper->getLogicMaxX();
1143 double fMaxY = m_pPosHelper->getLogicMaxY();
1144 double fMaxZ = m_pPosHelper->getLogicMaxZ();
1146 double fXStart = fMinX;
1147 double fYStart = fMinY;
1148 double fZStart = fMinZ;
1149 double fXEnd = fXStart;
1150 double fYEnd = fYStart;
1151 double fZEnd = fZStart;
1153 double fXOnXPlane = fMinX;
1154 double fXOther = fMaxX;
1155 int nDifferentValue = !m_pPosHelper->isMathematicalOrientationX() ? -1 : 1;
1156 if( !m_pPosHelper->isSwapXAndY() )
1157 nDifferentValue *= (CuboidPlanePosition_Left != m_eLeftWallPos) ? -1 : 1;
1158 else
1159 nDifferentValue *= (CuboidPlanePosition_Bottom != m_eBottomPos) ? -1 : 1;
1160 if( nDifferentValue<0 )
1162 fXOnXPlane = fMaxX;
1163 fXOther = fMinX;
1166 double fYOnYPlane = fMinY;
1167 double fYOther = fMaxY;
1168 nDifferentValue = !m_pPosHelper->isMathematicalOrientationY() ? -1 : 1;
1169 if( !m_pPosHelper->isSwapXAndY() )
1170 nDifferentValue *= (CuboidPlanePosition_Bottom != m_eBottomPos) ? -1 : 1;
1171 else
1172 nDifferentValue *= (CuboidPlanePosition_Left != m_eLeftWallPos) ? -1 : 1;
1173 if( nDifferentValue<0 )
1175 fYOnYPlane = fMaxY;
1176 fYOther = fMinY;
1179 double fZOnZPlane = fMaxZ;
1180 double fZOther = fMinZ;
1181 nDifferentValue = !m_pPosHelper->isMathematicalOrientationZ() ? -1 : 1;
1182 nDifferentValue *= (CuboidPlanePosition_Back != m_eBackWallPos) ? -1 : 1;
1183 if( nDifferentValue<0 )
1185 fZOnZPlane = fMinZ;
1186 fZOther = fMaxZ;
1189 if( 0==m_nDimensionIndex ) //x-axis
1191 if( fCrossesOtherAxis < fMinY )
1192 fCrossesOtherAxis = fMinY;
1193 else if( fCrossesOtherAxis > fMaxY )
1194 fCrossesOtherAxis = fMaxY;
1196 fYStart = fYEnd = fCrossesOtherAxis;
1197 fXEnd=m_pPosHelper->getLogicMaxX();
1199 if(3==m_nDimension)
1201 if( AxisHelper::isAxisPositioningEnabled() )
1203 if( ::rtl::math::approxEqual( fYOther, fYStart) )
1204 fZStart = fZEnd = fZOnZPlane;
1205 else
1206 fZStart = fZEnd = fZOther;
1208 else
1210 rStart = getScreenPosition( fXStart, fYStart, fZStart );
1211 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd );
1213 double fDeltaX = rEnd.getX() - rStart.getX();
1214 double fDeltaY = rEnd.getY() - rStart.getY();
1216 //only those points are candidates which are lying on exactly one wall as these are outer edges
1217 tScreenPosAndLogicPosList aPosList;
1218 aPosList.push_back( getScreenPosAndLogicPos( fMinX, fYOnYPlane, fZOther ) );
1219 aPosList.push_back( getScreenPosAndLogicPos( fMinX, fYOther, fZOnZPlane ) );
1221 if( fabs(fDeltaY) > fabs(fDeltaX) )
1223 rAlignment.meAlignment = LABEL_ALIGN_LEFT;
1224 //choose most left positions
1225 ::std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() );
1226 rAlignment.mfLabelDirection = (fDeltaY < 0) ? -1.0 : 1.0;
1228 else
1230 rAlignment.meAlignment = LABEL_ALIGN_BOTTOM;
1231 //choose most bottom positions
1232 ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() );
1233 rAlignment.mfLabelDirection = (fDeltaX < 0) ? -1.0 : 1.0;
1235 ScreenPosAndLogicPos aBestPos( aPosList[0] );
1236 fYStart = fYEnd = aBestPos.fLogicY;
1237 fZStart = fZEnd = aBestPos.fLogicZ;
1238 if( !m_pPosHelper->isMathematicalOrientationX() )
1239 rAlignment.mfLabelDirection *= -1.0;
1241 }//end 3D x axis
1243 else if( 1==m_nDimensionIndex ) //y-axis
1245 if( fCrossesOtherAxis < fMinX )
1246 fCrossesOtherAxis = fMinX;
1247 else if( fCrossesOtherAxis > fMaxX )
1248 fCrossesOtherAxis = fMaxX;
1250 fXStart = fXEnd = fCrossesOtherAxis;
1251 fYEnd=m_pPosHelper->getLogicMaxY();
1253 if(3==m_nDimension)
1255 if( AxisHelper::isAxisPositioningEnabled() )
1257 if( ::rtl::math::approxEqual( fXOther, fXStart) )
1258 fZStart = fZEnd = fZOnZPlane;
1259 else
1260 fZStart = fZEnd = fZOther;
1262 else
1264 rStart = getScreenPosition( fXStart, fYStart, fZStart );
1265 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd );
1267 double fDeltaX = rEnd.getX() - rStart.getX();
1268 double fDeltaY = rEnd.getY() - rStart.getY();
1270 //only those points are candidates which are lying on exactly one wall as these are outer edges
1271 tScreenPosAndLogicPosList aPosList;
1272 aPosList.push_back( getScreenPosAndLogicPos( fXOnXPlane, fMinY, fZOther ) );
1273 aPosList.push_back( getScreenPosAndLogicPos( fXOther, fMinY, fZOnZPlane ) );
1275 if( fabs(fDeltaY) > fabs(fDeltaX) )
1277 rAlignment.meAlignment = LABEL_ALIGN_LEFT;
1278 //choose most left positions
1279 ::std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() );
1280 rAlignment.mfLabelDirection = (fDeltaY < 0) ? -1.0 : 1.0;
1282 else
1284 rAlignment.meAlignment = LABEL_ALIGN_BOTTOM;
1285 //choose most bottom positions
1286 ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() );
1287 rAlignment.mfLabelDirection = (fDeltaX < 0) ? -1.0 : 1.0;
1289 ScreenPosAndLogicPos aBestPos( aPosList[0] );
1290 fXStart = fXEnd = aBestPos.fLogicX;
1291 fZStart = fZEnd = aBestPos.fLogicZ;
1292 if( !m_pPosHelper->isMathematicalOrientationY() )
1293 rAlignment.mfLabelDirection *= -1.0;
1295 }//end 3D y axis
1297 else //z-axis
1299 fZEnd = m_pPosHelper->getLogicMaxZ();
1300 if( AxisHelper::isAxisPositioningEnabled() )
1302 if( !m_aAxisProperties.m_bSwapXAndY )
1304 if( fCrossesOtherAxis < fMinY )
1305 fCrossesOtherAxis = fMinY;
1306 else if( fCrossesOtherAxis > fMaxY )
1307 fCrossesOtherAxis = fMaxY;
1308 fYStart = fYEnd = fCrossesOtherAxis;
1310 if( ::rtl::math::approxEqual( fYOther, fYStart) )
1311 fXStart = fXEnd = fXOnXPlane;
1312 else
1313 fXStart = fXEnd = fXOther;
1315 else
1317 if( fCrossesOtherAxis < fMinX )
1318 fCrossesOtherAxis = fMinX;
1319 else if( fCrossesOtherAxis > fMaxX )
1320 fCrossesOtherAxis = fMaxX;
1321 fXStart = fXEnd = fCrossesOtherAxis;
1323 if( ::rtl::math::approxEqual( fXOther, fXStart) )
1324 fYStart = fYEnd = fYOnYPlane;
1325 else
1326 fYStart = fYEnd = fYOther;
1329 else
1331 if( !m_pPosHelper->isSwapXAndY() )
1333 fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMinX();
1334 fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMinY() : m_pPosHelper->getLogicMaxY();
1336 else
1338 fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMaxX();
1339 fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMaxY() : m_pPosHelper->getLogicMinY();
1342 if(3==m_nDimension)
1344 rStart = getScreenPosition( fXStart, fYStart, fZStart );
1345 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd );
1347 double fDeltaX = rEnd.getX() - rStart.getX();
1349 //only those points are candidates which are lying on exactly one wall as these are outer edges
1350 tScreenPosAndLogicPosList aPosList;
1351 aPosList.push_back( getScreenPosAndLogicPos( fXOther, fYOnYPlane, fMinZ ) );
1352 aPosList.push_back( getScreenPosAndLogicPos( fXOnXPlane, fYOther, fMinZ ) );
1354 ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() );
1355 ScreenPosAndLogicPos aBestPos( aPosList[0] );
1356 ScreenPosAndLogicPos aNotSoGoodPos( aPosList[1] );
1358 //choose most bottom positions
1359 if( !::rtl::math::approxEqual( fDeltaX, 0.0 ) ) // prefer left-right alignments
1361 if( aBestPos.aScreenPos.getX() > aNotSoGoodPos.aScreenPos.getX() )
1362 rAlignment.meAlignment = LABEL_ALIGN_RIGHT;
1363 else
1364 rAlignment.meAlignment = LABEL_ALIGN_LEFT;
1366 else
1368 if( aBestPos.aScreenPos.getY() > aNotSoGoodPos.aScreenPos.getY() )
1369 rAlignment.meAlignment = LABEL_ALIGN_BOTTOM;
1370 else
1371 rAlignment.meAlignment = LABEL_ALIGN_TOP;
1374 rAlignment.mfLabelDirection = (fDeltaX < 0) ? -1.0 : 1.0;
1375 if( !m_pPosHelper->isMathematicalOrientationZ() )
1376 rAlignment.mfLabelDirection *= -1.0;
1378 fXStart = fXEnd = aBestPos.fLogicX;
1379 fYStart = fYEnd = aBestPos.fLogicY;
1381 }//end 3D z axis
1384 rStart = getScreenPosition( fXStart, fYStart, fZStart );
1385 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd );
1387 if(3==m_nDimension && !AxisHelper::isAxisPositioningEnabled() )
1388 rAlignment.mfInnerTickDirection = rAlignment.mfLabelDirection;//to behave like before
1390 if(3==m_nDimension && AxisHelper::isAxisPositioningEnabled() )
1392 double fDeltaX = rEnd.getX() - rStart.getX();
1393 double fDeltaY = rEnd.getY() - rStart.getY();
1395 if( 2==m_nDimensionIndex )
1397 if( m_eLeftWallPos != CuboidPlanePosition_Left )
1399 rAlignment.mfLabelDirection *= -1.0;
1400 rAlignment.mfInnerTickDirection *= -1.0;
1403 rAlignment.meAlignment =
1404 (rAlignment.mfLabelDirection < 0) ?
1405 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT;
1407 if( ( fDeltaY<0 && m_aScale.Orientation == chart2::AxisOrientation_REVERSE ) ||
1408 ( fDeltaY>0 && m_aScale.Orientation == chart2::AxisOrientation_MATHEMATICAL ) )
1409 rAlignment.meAlignment =
1410 (rAlignment.meAlignment == LABEL_ALIGN_RIGHT) ?
1411 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT;
1413 else if( fabs(fDeltaY) > fabs(fDeltaX) )
1415 if( m_eBackWallPos != CuboidPlanePosition_Back )
1417 rAlignment.mfLabelDirection *= -1.0;
1418 rAlignment.mfInnerTickDirection *= -1.0;
1421 rAlignment.meAlignment =
1422 (rAlignment.mfLabelDirection < 0) ?
1423 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT;
1425 if( ( fDeltaY<0 && m_aScale.Orientation == chart2::AxisOrientation_REVERSE ) ||
1426 ( fDeltaY>0 && m_aScale.Orientation == chart2::AxisOrientation_MATHEMATICAL ) )
1427 rAlignment.meAlignment =
1428 (rAlignment.meAlignment == LABEL_ALIGN_RIGHT) ?
1429 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT;
1431 else
1433 if( m_eBackWallPos != CuboidPlanePosition_Back )
1435 rAlignment.mfLabelDirection *= -1.0;
1436 rAlignment.mfInnerTickDirection *= -1.0;
1439 rAlignment.meAlignment =
1440 (rAlignment.mfLabelDirection < 0) ?
1441 LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM;
1443 if( ( fDeltaX>0 && m_aScale.Orientation == chart2::AxisOrientation_REVERSE ) ||
1444 ( fDeltaX<0 && m_aScale.Orientation == chart2::AxisOrientation_MATHEMATICAL ) )
1445 rAlignment.meAlignment =
1446 (rAlignment.meAlignment == LABEL_ALIGN_TOP) ?
1447 LABEL_ALIGN_BOTTOM : LABEL_ALIGN_TOP;
1452 TickFactory* VCartesianAxis::createTickFactory()
1454 return createTickFactory2D();
1457 TickFactory2D* VCartesianAxis::createTickFactory2D()
1459 AxisLabelAlignment aLabelAlign = m_aAxisProperties.maLabelAlignment;
1460 B2DVector aStart, aEnd;
1461 get2DAxisMainLine(aStart, aEnd, aLabelAlign, getAxisIntersectionValue());
1463 B2DVector aLabelLineStart, aLabelLineEnd;
1464 get2DAxisMainLine(aLabelLineStart, aLabelLineEnd, aLabelAlign, getLabelLineIntersectionValue());
1465 m_aAxisProperties.maLabelAlignment = aLabelAlign;
1467 return new TickFactory2D( m_aScale, m_aIncrement, aStart, aEnd, aLabelLineStart-aStart );
1470 void lcl_hideIdenticalScreenValues( TickIter& rTickIter )
1472 TickInfo* pPrevTickInfo = rTickIter.firstInfo();
1473 if (!pPrevTickInfo)
1474 return;
1476 pPrevTickInfo->bPaintIt = true;
1477 for( TickInfo* pTickInfo = rTickIter.nextInfo(); pTickInfo; pTickInfo = rTickIter.nextInfo())
1479 pTickInfo->bPaintIt = (pTickInfo->aTickScreenPosition != pPrevTickInfo->aTickScreenPosition);
1480 pPrevTickInfo = pTickInfo;
1484 //'hide' tickmarks with identical screen values in aAllTickInfos
1485 void VCartesianAxis::hideIdenticalScreenValues( TickInfoArraysType& rTickInfos ) const
1487 if( isComplexCategoryAxis() || isDateAxis() )
1489 sal_Int32 nCount = rTickInfos.size();
1490 for( sal_Int32 nN=0; nN<nCount; nN++ )
1492 PureTickIter aTickIter( rTickInfos[nN] );
1493 lcl_hideIdenticalScreenValues( aTickIter );
1496 else
1498 EquidistantTickIter aTickIter( rTickInfos, m_aIncrement, 0, -1 );
1499 lcl_hideIdenticalScreenValues( aTickIter );
1503 sal_Int32 VCartesianAxis::estimateMaximumAutoMainIncrementCount()
1505 sal_Int32 nRet = 10;
1507 if( m_nMaximumTextWidthSoFar==0 && m_nMaximumTextHeightSoFar==0 )
1508 return nRet;
1510 B2DVector aStart, aEnd;
1511 AxisLabelAlignment aLabelAlign = m_aAxisProperties.maLabelAlignment;
1512 get2DAxisMainLine(aStart, aEnd, aLabelAlign, getAxisIntersectionValue());
1513 m_aAxisProperties.maLabelAlignment = aLabelAlign;
1515 sal_Int32 nMaxHeight = static_cast<sal_Int32>(fabs(aEnd.getY()-aStart.getY()));
1516 sal_Int32 nMaxWidth = static_cast<sal_Int32>(fabs(aEnd.getX()-aStart.getX()));
1518 sal_Int32 nTotalAvailable = nMaxHeight;
1519 sal_Int32 nSingleNeeded = m_nMaximumTextHeightSoFar;
1521 //for horizontal axis:
1522 if( (m_nDimensionIndex == 0 && !m_aAxisProperties.m_bSwapXAndY)
1523 || (m_nDimensionIndex == 1 && m_aAxisProperties.m_bSwapXAndY) )
1525 nTotalAvailable = nMaxWidth;
1526 nSingleNeeded = m_nMaximumTextWidthSoFar;
1529 if( nSingleNeeded>0 )
1530 nRet = nTotalAvailable/nSingleNeeded;
1532 return nRet;
1535 void VCartesianAxis::doStaggeringOfLabels( const AxisLabelProperties& rAxisLabelProperties, TickFactory2D* pTickFactory2D )
1537 if( !pTickFactory2D )
1538 return;
1540 if( isComplexCategoryAxis() )
1542 sal_Int32 nTextLevelCount = getTextLevelCount();
1543 B2DVector aCummulatedLabelsDistance(0,0);
1544 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ )
1546 boost::scoped_ptr<TickIter> apTickIter(createLabelTickIterator(nTextLevel));
1547 if (apTickIter)
1549 double fRotationAngleDegree = m_aAxisLabelProperties.fRotationAngleDegree;
1550 if( nTextLevel>0 )
1552 lcl_shiftLabels( *apTickIter.get(), aCummulatedLabelsDistance );
1553 fRotationAngleDegree = 0.0;
1555 aCummulatedLabelsDistance += lcl_getLabelsDistance( *apTickIter.get()
1556 , pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties )
1557 , fRotationAngleDegree );
1561 else if (rAxisLabelProperties.isStaggered())
1563 if( !m_aAllTickInfos.empty() )
1565 LabelIterator aInnerIter( m_aAllTickInfos[0], rAxisLabelProperties.eStaggering, true );
1566 LabelIterator aOuterIter( m_aAllTickInfos[0], rAxisLabelProperties.eStaggering, false );
1568 lcl_shiftLabels( aOuterIter
1569 , lcl_getLabelsDistance( aInnerIter
1570 , pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties ), 0.0 ) );
1575 void VCartesianAxis::createLabels()
1577 if( !prepareShapeCreation() )
1578 return;
1580 //create labels
1581 if (!m_aAxisProperties.m_bDisplayLabels)
1582 return;
1584 boost::scoped_ptr< TickFactory2D > apTickFactory2D( this->createTickFactory2D() );
1585 TickFactory2D* pTickFactory2D = apTickFactory2D.get();
1586 if( !pTickFactory2D )
1587 return;
1589 //get the transformed screen values for all tickmarks in aAllTickInfos
1590 pTickFactory2D->updateScreenValues( m_aAllTickInfos );
1591 //'hide' tickmarks with identical screen values in aAllTickInfos
1592 hideIdenticalScreenValues( m_aAllTickInfos );
1594 removeTextShapesFromTicks();
1596 //create tick mark text shapes
1597 sal_Int32 nTextLevelCount = getTextLevelCount();
1598 sal_Int32 nScreenDistanceBetweenTicks = -1;
1599 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ )
1601 boost::scoped_ptr< TickIter > apTickIter(createLabelTickIterator( nTextLevel ));
1602 if(apTickIter)
1604 if(nTextLevel==0)
1606 nScreenDistanceBetweenTicks = TickFactory2D::getTickScreenDistance( *apTickIter.get() );
1607 if( nTextLevelCount>1 )
1608 nScreenDistanceBetweenTicks*=2; //the above used tick iter does contain also the sub ticks -> thus the given distance is only the half
1611 AxisLabelProperties aComplexProps(m_aAxisLabelProperties);
1612 if( m_aAxisProperties.m_bComplexCategories )
1614 aComplexProps.bLineBreakAllowed = true;
1615 aComplexProps.bOverlapAllowed = !::rtl::math::approxEqual( aComplexProps.fRotationAngleDegree, 0.0 );
1618 AxisLabelProperties& rAxisLabelProperties = m_aAxisProperties.m_bComplexCategories ? aComplexProps : m_aAxisLabelProperties;
1619 while( !createTextShapes( m_xTextTarget, *apTickIter.get(), rAxisLabelProperties, pTickFactory2D, nScreenDistanceBetweenTicks ) )
1624 doStaggeringOfLabels( m_aAxisLabelProperties, pTickFactory2D );
1627 void VCartesianAxis::createMaximumLabels()
1629 TrueGuard aRecordMaximumTextSize(m_bRecordMaximumTextSize);
1631 if( !prepareShapeCreation() )
1632 return;
1634 //create labels
1635 if (!m_aAxisProperties.m_bDisplayLabels)
1636 return;
1638 boost::scoped_ptr< TickFactory2D > apTickFactory2D( this->createTickFactory2D() );
1639 TickFactory2D* pTickFactory2D = apTickFactory2D.get();
1640 if( !pTickFactory2D )
1641 return;
1643 //get the transformed screen values for all tickmarks in aAllTickInfos
1644 pTickFactory2D->updateScreenValues( m_aAllTickInfos );
1646 //create tick mark text shapes
1647 //@todo: iterate through all tick depth which should be labeled
1649 AxisLabelProperties aAxisLabelProperties( m_aAxisLabelProperties );
1650 if( isAutoStaggeringOfLabelsAllowed( aAxisLabelProperties, pTickFactory2D->isHorizontalAxis(), pTickFactory2D->isVerticalAxis() ) )
1651 aAxisLabelProperties.eStaggering = STAGGER_EVEN;
1653 aAxisLabelProperties.bOverlapAllowed = true;
1654 aAxisLabelProperties.bLineBreakAllowed = false;
1655 sal_Int32 nTextLevelCount = getTextLevelCount();
1656 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ )
1658 boost::scoped_ptr< TickIter > apTickIter(createMaximumLabelTickIterator( nTextLevel ));
1659 if(apTickIter)
1661 while( !createTextShapes( m_xTextTarget, *apTickIter.get(), aAxisLabelProperties, pTickFactory2D, -1 ) )
1666 doStaggeringOfLabels( aAxisLabelProperties, pTickFactory2D );
1669 void VCartesianAxis::updatePositions()
1671 //update positions of labels
1672 if (!m_aAxisProperties.m_bDisplayLabels)
1673 return;
1675 boost::scoped_ptr< TickFactory2D > apTickFactory2D( this->createTickFactory2D() );
1676 TickFactory2D* pTickFactory2D = apTickFactory2D.get();
1677 if( !pTickFactory2D )
1678 return;
1680 //update positions of all existing text shapes
1681 pTickFactory2D->updateScreenValues( m_aAllTickInfos );
1683 TickInfoArraysType::iterator aDepthIter = m_aAllTickInfos.begin();
1684 const TickInfoArraysType::const_iterator aDepthEnd = m_aAllTickInfos.end();
1685 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd; ++aDepthIter, nDepth++ )
1687 TickInfoArrayType::iterator aTickIter = aDepthIter->begin();
1688 const TickInfoArrayType::const_iterator aTickEnd = aDepthIter->end();
1689 for( ; aTickIter != aTickEnd; ++aTickIter )
1691 TickInfo& rTickInfo = (*aTickIter);
1692 Reference< drawing::XShape > xShape2DText( rTickInfo.xTextShape );
1693 if( xShape2DText.is() )
1695 B2DVector aTextToTickDistance( pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, true ) );
1696 B2DVector aTickScreenPos2D( rTickInfo.aTickScreenPosition );
1697 aTickScreenPos2D += aTextToTickDistance;
1698 awt::Point aAnchorScreenPosition2D(
1699 static_cast<sal_Int32>(aTickScreenPos2D.getX())
1700 ,static_cast<sal_Int32>(aTickScreenPos2D.getY()));
1702 double fRotationAngleDegree = m_aAxisLabelProperties.fRotationAngleDegree;
1703 if( nDepth > 0 )
1705 /* Multi-level Labels: default to 0 or 90 */
1706 if( pTickFactory2D->isHorizontalAxis() )
1707 fRotationAngleDegree = 0.0;
1708 else
1709 fRotationAngleDegree = 90;
1712 // #i78696# use mathematically correct rotation now
1713 const double fRotationAnglePi(fRotationAngleDegree * (F_PI / -180.0));
1714 uno::Any aATransformation = AbstractShapeFactory::makeTransformation(aAnchorScreenPosition2D, fRotationAnglePi);
1716 //set new position
1717 uno::Reference< beans::XPropertySet > xProp( xShape2DText, uno::UNO_QUERY );
1718 if( xProp.is() )
1722 xProp->setPropertyValue( "Transformation", aATransformation );
1724 catch( const uno::Exception& e )
1726 ASSERT_EXCEPTION( e );
1730 //correctPositionForRotation
1731 LabelPositionHelper::correctPositionForRotation( xShape2DText
1732 , m_aAxisProperties.maLabelAlignment.meAlignment, fRotationAngleDegree, m_aAxisProperties.m_bComplexCategories );
1737 doStaggeringOfLabels( m_aAxisLabelProperties, pTickFactory2D );
1740 void VCartesianAxis::createTickMarkLineShapes( TickInfoArrayType& rTickInfos, const TickmarkProperties& rTickmarkProperties, TickFactory2D& rTickFactory2D, bool bOnlyAtLabels )
1742 sal_Int32 nPointCount = rTickInfos.size();
1743 drawing::PointSequenceSequence aPoints(2*nPointCount);
1745 TickInfoArrayType::const_iterator aTickIter = rTickInfos.begin();
1746 const TickInfoArrayType::const_iterator aTickEnd = rTickInfos.end();
1747 sal_Int32 nN = 0;
1748 for( ; aTickIter != aTickEnd; ++aTickIter )
1750 if( !(*aTickIter).bPaintIt )
1751 continue;
1753 bool bTicksAtLabels = ( m_aAxisProperties.m_eTickmarkPos != ::com::sun::star::chart::ChartAxisMarkPosition_AT_AXIS );
1754 double fInnerDirectionSign = m_aAxisProperties.maLabelAlignment.mfInnerTickDirection;
1755 if( bTicksAtLabels && m_aAxisProperties.m_eLabelPos == ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END )
1756 fInnerDirectionSign *= -1.0;
1757 bTicksAtLabels = bTicksAtLabels || bOnlyAtLabels;
1758 //add ticks at labels:
1759 rTickFactory2D.addPointSequenceForTickLine( aPoints, nN++, (*aTickIter).fScaledTickValue
1760 , fInnerDirectionSign , rTickmarkProperties, bTicksAtLabels );
1761 //add ticks at axis (without labels):
1762 if( !bOnlyAtLabels && m_aAxisProperties.m_eTickmarkPos == ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS_AND_AXIS )
1763 rTickFactory2D.addPointSequenceForTickLine( aPoints, nN++, (*aTickIter).fScaledTickValue
1764 , m_aAxisProperties.maLabelAlignment.mfInnerTickDirection, rTickmarkProperties, !bTicksAtLabels );
1766 aPoints.realloc(nN);
1767 m_pShapeFactory->createLine2D( m_xGroupShape_Shapes, aPoints
1768 , &rTickmarkProperties.aLineProperties );
1771 void VCartesianAxis::createShapes()
1773 if( !prepareShapeCreation() )
1774 return;
1776 boost::scoped_ptr< TickFactory2D > apTickFactory2D( this->createTickFactory2D() );
1777 TickFactory2D* pTickFactory2D = apTickFactory2D.get();
1778 if( !pTickFactory2D )
1779 return;
1781 //create line shapes
1782 if(2==m_nDimension)
1784 //create extra long ticks to separate complex categories (create them only there where the labels are)
1785 if( isComplexCategoryAxis() )
1787 TickInfoArraysType aComplexTickInfos;
1788 createAllTickInfosFromComplexCategories( aComplexTickInfos, true );
1789 pTickFactory2D->updateScreenValues( aComplexTickInfos );
1790 hideIdenticalScreenValues( aComplexTickInfos );
1792 ::std::vector<TickmarkProperties> aTickmarkPropertiesList;
1793 static bool bIncludeSpaceBetweenTickAndText = false;
1794 sal_Int32 nOffset = static_cast<sal_Int32>(pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, false, bIncludeSpaceBetweenTickAndText ).getLength());
1795 sal_Int32 nTextLevelCount = getTextLevelCount();
1796 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ )
1798 boost::scoped_ptr< TickIter > apTickIter(createLabelTickIterator( nTextLevel ));
1799 if( apTickIter )
1801 double fRotationAngleDegree = m_aAxisLabelProperties.fRotationAngleDegree;
1802 B2DVector aLabelsDistance( lcl_getLabelsDistance( *apTickIter.get(), pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, false ), fRotationAngleDegree ) );
1803 sal_Int32 nCurrentLength = static_cast<sal_Int32>(aLabelsDistance.getLength());
1804 aTickmarkPropertiesList.push_back( m_aAxisProperties.makeTickmarkPropertiesForComplexCategories( nOffset + nCurrentLength, 0, nTextLevel ) );
1805 nOffset += nCurrentLength;
1809 sal_Int32 nTickmarkPropertiesCount = aTickmarkPropertiesList.size();
1810 TickInfoArraysType::iterator aDepthIter = aComplexTickInfos.begin();
1811 const TickInfoArraysType::const_iterator aDepthEnd = aComplexTickInfos.end();
1812 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd && nDepth < nTickmarkPropertiesCount; ++aDepthIter, nDepth++ )
1814 if(nDepth==0 && !m_aAxisProperties.m_nMajorTickmarks)
1815 continue;
1816 createTickMarkLineShapes( *aDepthIter, aTickmarkPropertiesList[nDepth], *pTickFactory2D, true /*bOnlyAtLabels*/ );
1819 //create normal ticks for major and minor intervals
1821 TickInfoArraysType aUnshiftedTickInfos;
1822 if( m_aScale.ShiftedCategoryPosition )// if ShiftedCategoryPosition==true the tickmarks in m_aAllTickInfos are shifted
1824 pTickFactory2D->getAllTicks( aUnshiftedTickInfos );
1825 pTickFactory2D->updateScreenValues( aUnshiftedTickInfos );
1826 hideIdenticalScreenValues( aUnshiftedTickInfos );
1828 TickInfoArraysType& rAllTickInfos = m_aScale.ShiftedCategoryPosition ? aUnshiftedTickInfos : m_aAllTickInfos;
1830 TickInfoArraysType::iterator aDepthIter = rAllTickInfos.begin();
1831 const TickInfoArraysType::const_iterator aDepthEnd = rAllTickInfos.end();
1832 if(aDepthIter == aDepthEnd)//no tickmarks at all
1833 return;
1835 sal_Int32 nTickmarkPropertiesCount = m_aAxisProperties.m_aTickmarkPropertiesList.size();
1836 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd && nDepth < nTickmarkPropertiesCount; ++aDepthIter, nDepth++ )
1837 createTickMarkLineShapes( *aDepthIter, m_aAxisProperties.m_aTickmarkPropertiesList[nDepth], *pTickFactory2D, false /*bOnlyAtLabels*/ );
1839 //create axis main lines
1840 //it serves also as the handle shape for the axis selection
1842 drawing::PointSequenceSequence aPoints(1);
1843 apTickFactory2D->createPointSequenceForAxisMainLine( aPoints );
1844 Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D(
1845 m_xGroupShape_Shapes, aPoints
1846 , &m_aAxisProperties.m_aLineProperties );
1847 //because of this name this line will be used for marking the axis
1848 ::chart::AbstractShapeFactory::setShapeName( xShape, "MarkHandles" );
1850 //create an additional line at NULL
1851 if( !AxisHelper::isAxisPositioningEnabled() )
1853 double fExtraLineCrossesOtherAxis = getExtraLineIntersectionValue();
1854 if (!rtl::math::isNan(fExtraLineCrossesOtherAxis))
1856 B2DVector aStart, aEnd;
1857 AxisLabelAlignment aLabelAlign = m_aAxisProperties.maLabelAlignment;
1858 get2DAxisMainLine(aStart, aEnd, aLabelAlign, fExtraLineCrossesOtherAxis);
1859 m_aAxisProperties.maLabelAlignment = aLabelAlign;
1860 drawing::PointSequenceSequence aPoints( lcl_makePointSequence(aStart,aEnd) );
1861 Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D(
1862 m_xGroupShape_Shapes, aPoints, &m_aAxisProperties.m_aLineProperties );
1867 //createLabels();
1870 } //namespace chart
1872 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */