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