1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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>
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
;
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
)
67 m_pPosHelper
= pPosHelper
;
69 m_pPosHelper
= new PlottingPositionHelper();
72 VCartesianAxis::~VCartesianAxis()
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
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
);
106 bool lcl_doesShapeOverlapWithTickmark( const Reference
< drawing::XShape
>& xShape
107 , double fRotationAngleDegree
108 , const basegfx::B2DVector
& rTickScreenPosition
109 , bool bIsHorizontalAxis
, bool bIsVerticalAxis
)
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() )
160 ::basegfx::B2DRectangle
aRect1( BaseGFXHelper::makeRectangle( awt::Point(0,0), xShape1
->getSize()));
161 ::basegfx::B2DRectangle
aRect2( BaseGFXHelper::makeRectangle( awt::Point(0,0), xShape2
->getSize()));
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
)
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
210 LabelIterator( TickInfoArrayType
& rTickInfoVector
211 , const AxisLabelStaggering eAxisLabelStaggering
214 virtual TickInfo
* firstInfo() SAL_OVERRIDE
;
215 virtual TickInfo
* nextInfo() SAL_OVERRIDE
;
218 PureTickIter m_aPureTickIter
;
219 const AxisLabelStaggering m_eAxisLabelStaggering
;
223 LabelIterator::LabelIterator( TickInfoArrayType
& rTickInfoVector
224 , const AxisLabelStaggering eAxisLabelStaggering
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();
239 if( (STAGGER_EVEN
==m_eAxisLabelStaggering
&& m_bInnerLine
)
241 (STAGGER_ODD
==m_eAxisLabelStaggering
&& !m_bInnerLine
)
246 pTickInfo
= m_aPureTickIter
.nextInfo();
247 while( pTickInfo
&& !pTickInfo
->xTextShape
.is() );
254 TickInfo
* LabelIterator::nextInfo()
256 TickInfo
* pTickInfo
= NULL
;
259 pTickInfo
= m_aPureTickIter
.nextInfo();
260 while( pTickInfo
&& !pTickInfo
->xTextShape
.is() );
262 if( STAGGER_EVEN
==m_eAxisLabelStaggering
263 || STAGGER_ODD
==m_eAxisLabelStaggering
)
267 pTickInfo
= m_aPureTickIter
.nextInfo();
268 while( pTickInfo
&& !pTickInfo
->xTextShape
.is() );
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
280 sal_Int32 nDistanceTickToText
= static_cast<sal_Int32
>( rDistanceTickToText
.getLength() );
281 if( nDistanceTickToText
==0.0)
284 B2DVector
aStaggerDirection(rDistanceTickToText
);
285 aStaggerDirection
.normalize();
287 sal_Int32 nDistance
=0;
288 Reference
< drawing::XShape
> xShape2DText(NULL
);
289 for( TickInfo
* pTickInfo
= rIter
.firstInfo()
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
);
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
;
313 void lcl_shiftLabels( TickIter
& rIter
, const B2DVector
& rStaggerDistance
)
315 if(rStaggerDistance
.getLength()==0.0)
317 Reference
< drawing::XShape
> xShape2DText(NULL
);
318 for( TickInfo
* pTickInfo
= rIter
.firstInfo()
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
)
338 SvxShape
* pShape
= SvxShape::getImplementation(xShape
);
339 SvxShapeText
* pShapeText
= dynamic_cast<SvxShapeText
*>(pShape
);
343 SvxTextEditSource
* pTextEditSource
= dynamic_cast<SvxTextEditSource
*>(pShapeText
->GetEditSource());
344 if (!pTextEditSource
)
347 pTextEditSource
->UpdateOutliner();
348 SvxTextForwarder
* pTextForwarder
= pTextEditSource
->GetTextForwarder();
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
) )
375 OUString
getTextLabelString(
376 const FixedNumberFormatter
& rFixedNumberFormatter
, const uno::Sequence
<OUString
>* pCategories
,
377 const TickInfo
* pTickInfo
, bool bComplexCat
, sal_Int32
& rExtraColor
, bool& rHasExtraColor
)
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
];
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
425 MaxLabelTickIter( TickInfoArrayType
& rTickInfoVector
, size_t nLongestLabelIndex
);
426 virtual ~MaxLabelTickIter();
428 virtual TickInfo
* firstInfo() SAL_OVERRIDE
;
429 virtual TickInfo
* nextInfo() SAL_OVERRIDE
;
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
)
459 m_aValidIndices
.push_back(nLongestLabelIndex
);
463 MaxLabelTickIter::~MaxLabelTickIter()
467 TickInfo
* MaxLabelTickIter::firstInfo()
470 if (m_nCurrentIndex
< m_aValidIndices
.size())
471 return &m_rTickInfoVector
[m_aValidIndices
[m_nCurrentIndex
]];
475 TickInfo
* MaxLabelTickIter::nextInfo()
478 if (m_nCurrentIndex
< m_aValidIndices
.size())
479 return &m_rTickInfoVector
[m_aValidIndices
[m_nCurrentIndex
]];
483 bool VCartesianAxis::isBreakOfLabelsAllowed(
484 const AxisLabelProperties
& rAxisLabelProperties
, bool bIsHorizontalAxis
) const
486 if( m_aTextLabels
.getLength() > 100 )
488 if( !rAxisLabelProperties
.bLineBreakAllowed
)
490 if( rAxisLabelProperties
.bStackCharacters
)
492 //no break for value axis
493 if( !m_bUseTextLabels
)
495 if( !::rtl::math::approxEqual( rAxisLabelProperties
.fRotationAngleDegree
, 0.0 ) )
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
)
506 if( rAxisLabelProperties
.bOverlapAllowed
)
508 if( rAxisLabelProperties
.bLineBreakAllowed
) //auto line break or auto staggering, doing both automatisms they may conflict...
510 if( !::rtl::math::approxEqual( rAxisLabelProperties
.fRotationAngleDegree
, 0.0 ) )
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
;
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();
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
)
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
);
554 aTickInfo
.fScaledTickValue
= nCatIndex
+ 1.0 + nCount
/2.0;
555 aTickInfo
.nFactorForLimitedTextWidth
= nCount
;
556 aTickInfo
.aText
= aCat
.Text
;
557 aTickInfoVector
.push_back(aTickInfo
);
559 if( nCatIndex
+ 1.0 >= m_aScale
.Maximum
)
562 rAllTickInfos
.push_back(aTickInfoVector
);
565 else //bShiftedPosition==false
567 rAllTickInfos
.clear();
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
)
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
);
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 );
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
] );
628 TickIter
* VCartesianAxis::createMaximumLabelTickIterator( sal_Int32 nTextLevel
)
630 if( isComplexCategoryAxis() || isDateAxis() )
632 return createLabelTickIterator( nTextLevel
); //mmmm maybe todo: create less than all texts here
638 if( !m_aAllTickInfos
.empty() )
640 size_t nLongestLabelIndex
= m_bUseTextLabels
? getIndexOfLongestLabel(m_aTextLabels
) : 0;
641 if (nLongestLabelIndex
>= m_aAllTickInfos
[0].size())
644 return new MaxLabelTickIter( m_aAllTickInfos
[0], nLongestLabelIndex
);
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
;
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;
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();
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
;
718 for( TickInfo
* pTickInfo
= rTickIter
.firstInfo()
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 )
729 //don't create labels for invisible ticks
730 if( !pTickInfo
->bPaintIt
)
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
) )
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
)
769 rAxisLabelProperties
.nRhythm
++;
770 removeShapesAtWrongRhythm( rTickIter
, rAxisLabelProperties
.nRhythm
, nTick
, xTarget
);
776 bool bHasExtraColor
=false;
777 sal_Int32 nExtraColor
=0;
779 OUString aLabel
= getTextLabelString(
780 aFixedNumberFormatter
, pCategories
, pTickInfo
, isComplexCategoryAxis(),
781 nExtraColor
, bHasExtraColor
);
784 *pColorAny
= uno::makeAny(bHasExtraColor
?nExtraColor
:nColor
);
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())
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();
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
) )
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;
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
;
866 // Try incrementing the tick interval and start over.
867 rAxisLabelProperties
.nRhythm
++;
868 removeShapesAtWrongRhythm( rTickIter
, rAxisLabelProperties
.nRhythm
, nTick
, xTarget
);
874 pPREPreviousVisibleTickInfo
= pPreviousVisibleTickInfo
;
875 pPreviousVisibleTickInfo
= pTickInfo
;
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();
907 *pColorAny
>>= nColor
;
909 uno::Any
* pLimitedSpaceAny
= PropertyMapper::getValuePointerForLimitedSpace(aPropValues
,aPropNames
,bLimitedHeight
);
911 const TickInfo
* pPreviousVisibleTickInfo
= NULL
;
912 const TickInfo
* pLastVisibleNeighbourTickInfo
= NULL
;
914 for( TickInfo
* pTickInfo
= rTickIter
.firstInfo()
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 )
924 //don't create labels for invisible ticks
925 if( !pTickInfo
->bPaintIt
)
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
)
944 rAxisLabelProperties
.nRhythm
++;
945 removeShapesAtWrongRhythm( rTickIter
, rAxisLabelProperties
.nRhythm
, nTick
, xTarget
);
950 bool bHasExtraColor
=false;
951 sal_Int32 nExtraColor
=0;
953 OUString aLabel
= getTextLabelString(
954 aFixedNumberFormatter
, pCategories
, pTickInfo
, isComplexCategoryAxis(),
955 nExtraColor
, bHasExtraColor
);
958 *pColorAny
= uno::makeAny(bHasExtraColor
?nExtraColor
:nColor
);
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())
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
) )
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;
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
;
1009 // Try incrementing the tick interval and start over.
1010 rAxisLabelProperties
.nRhythm
++;
1011 removeShapesAtWrongRhythm( rTickIter
, rAxisLabelProperties
.nRhythm
, nTick
, xTarget
);
1016 pPreviousVisibleTickInfo
= pTickInfo
;
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());
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
1057 rtl::math::setNan(&fNan
);
1059 if( !m_aAxisProperties
.m_pfExrtaLinePositionAtOtherAxis
)
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
)
1069 return *m_aAxisProperties
.m_pfExrtaLinePositionAtOtherAxis
;
1072 B2DVector
VCartesianAxis::getScreenPosition( double fLogicX
, double fLogicY
, double fLogicZ
) const
1074 B2DVector
aRet(0,0);
1078 drawing::Position3D aScenePos
= m_pPosHelper
->transformLogicToScene( fLogicX
, fLogicY
, fLogicZ
, true );
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
);
1093 OSL_FAIL("cannot calculate scrren position in VCartesianAxis::getScreenPosition");
1098 aRet
.setX( aScenePos
.PositionX
);
1099 aRet
.setY( aScenePos
.PositionY
);
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_
);
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;
1159 nDifferentValue
*= (CuboidPlanePosition_Bottom
!= m_eBottomPos
) ? -1 : 1;
1160 if( nDifferentValue
<0 )
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;
1172 nDifferentValue
*= (CuboidPlanePosition_Left
!= m_eLeftWallPos
) ? -1 : 1;
1173 if( nDifferentValue
<0 )
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 )
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();
1201 if( AxisHelper::isAxisPositioningEnabled() )
1203 if( ::rtl::math::approxEqual( fYOther
, fYStart
) )
1204 fZStart
= fZEnd
= fZOnZPlane
;
1206 fZStart
= fZEnd
= fZOther
;
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;
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;
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();
1255 if( AxisHelper::isAxisPositioningEnabled() )
1257 if( ::rtl::math::approxEqual( fXOther
, fXStart
) )
1258 fZStart
= fZEnd
= fZOnZPlane
;
1260 fZStart
= fZEnd
= fZOther
;
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;
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;
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
;
1313 fXStart
= fXEnd
= fXOther
;
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
;
1326 fYStart
= fYEnd
= fYOther
;
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();
1338 fXStart
= fXEnd
= m_pPosHelper
->isMathematicalOrientationX() ? m_pPosHelper
->getLogicMinX() : m_pPosHelper
->getLogicMaxX();
1339 fYStart
= fYEnd
= m_pPosHelper
->isMathematicalOrientationY() ? m_pPosHelper
->getLogicMaxY() : m_pPosHelper
->getLogicMinY();
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
;
1364 rAlignment
.meAlignment
= LABEL_ALIGN_LEFT
;
1368 if( aBestPos
.aScreenPos
.getY() > aNotSoGoodPos
.aScreenPos
.getY() )
1369 rAlignment
.meAlignment
= LABEL_ALIGN_BOTTOM
;
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
;
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
;
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();
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
);
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 )
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
;
1535 void VCartesianAxis::doStaggeringOfLabels( const AxisLabelProperties
& rAxisLabelProperties
, TickFactory2D
* pTickFactory2D
)
1537 if( !pTickFactory2D
)
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
));
1549 double fRotationAngleDegree
= m_aAxisLabelProperties
.fRotationAngleDegree
;
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() )
1581 if (!m_aAxisProperties
.m_bDisplayLabels
)
1584 boost::scoped_ptr
< TickFactory2D
> apTickFactory2D( this->createTickFactory2D() );
1585 TickFactory2D
* pTickFactory2D
= apTickFactory2D
.get();
1586 if( !pTickFactory2D
)
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
));
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() )
1635 if (!m_aAxisProperties
.m_bDisplayLabels
)
1638 boost::scoped_ptr
< TickFactory2D
> apTickFactory2D( this->createTickFactory2D() );
1639 TickFactory2D
* pTickFactory2D
= apTickFactory2D
.get();
1640 if( !pTickFactory2D
)
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
));
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
)
1675 boost::scoped_ptr
< TickFactory2D
> apTickFactory2D( this->createTickFactory2D() );
1676 TickFactory2D
* pTickFactory2D
= apTickFactory2D
.get();
1677 if( !pTickFactory2D
)
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
;
1705 /* Multi-level Labels: default to 0 or 90 */
1706 if( pTickFactory2D
->isHorizontalAxis() )
1707 fRotationAngleDegree
= 0.0;
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
);
1717 uno::Reference
< beans::XPropertySet
> xProp( xShape2DText
, uno::UNO_QUERY
);
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();
1748 for( ; aTickIter
!= aTickEnd
; ++aTickIter
)
1750 if( !(*aTickIter
).bPaintIt
)
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() )
1776 boost::scoped_ptr
< TickFactory2D
> apTickFactory2D( this->createTickFactory2D() );
1777 TickFactory2D
* pTickFactory2D
= apTickFactory2D
.get();
1778 if( !pTickFactory2D
)
1781 //create line shapes
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
));
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
)
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
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
);
1872 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */