LibreOffice Module chart2 (master) 1
VCartesianAxis.cxx
Go to the documentation of this file.
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 "VCartesianAxis.hxx"
22#include <ShapeFactory.hxx>
23#include <PropertyMapper.hxx>
26#include <BaseGFXHelper.hxx>
27#include <Axis.hxx>
28#include <AxisHelper.hxx>
31#include <com/sun/star/chart2/AxisType.hpp>
32#include <o3tl/safeint.hxx>
33#include <rtl/math.hxx>
35#include <tools/color.hxx>
36#include <svx/unoshape.hxx>
37#include <svx/unoshtxt.hxx>
38#include <VSeriesPlotter.hxx>
39#include <DataTableView.hxx>
40#include <ChartModel.hxx>
41
43
50
51#include <algorithm>
52#include <limits>
53#include <memory>
54
55using namespace ::com::sun::star;
56using ::com::sun::star::uno::Reference;
57using ::basegfx::B2DVector;
58using ::basegfx::B2DPolygon;
59using ::basegfx::B2DPolyPolygon;
60
61namespace chart {
62
64 , const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier
65 , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount
66 , PlottingPositionHelper* pPosHelper )//takes ownership
67 : VAxisBase( nDimensionIndex, nDimensionCount, rAxisProperties, xNumberFormatsSupplier )
68{
69 if( pPosHelper )
70 m_pPosHelper = pPosHelper;
71 else
73}
74
76{
77 delete m_pPosHelper;
78 m_pPosHelper = nullptr;
79}
80
82 const AxisLabelProperties& rAxisLabelProperties,
83 std::u16string_view rLabel,
84 const tNameSequence& rPropNames,
85 const tAnySequence& rPropValues,
86 const bool bIsHorizontalAxis )
87{
88 bool bTextHorizontal = rAxisLabelProperties.m_fRotationAngleDegree != 0.0;
89 bool bIsDirectionVertical = bIsHorizontalAxis && bTextHorizontal;
90 const sal_Int32 nFullSize = bIsDirectionVertical ? rAxisLabelProperties.m_aFontReferenceSize.Height : rAxisLabelProperties.m_aFontReferenceSize.Width;
91
92 if( !nFullSize || rLabel.empty() )
93 return;
94
95 const sal_Int32 nAvgCharWidth = rShape2DText.getSize().Width / rLabel.size();
96
97 sal_Int32 nMaxLabelsSize = bIsDirectionVertical ? rAxisLabelProperties.m_aMaximumSpaceForLabels.Height : rAxisLabelProperties.m_aMaximumSpaceForLabels.Width;
98
99 awt::Size aSizeAfterRotation = ShapeFactory::getSizeAfterRotation(rShape2DText, rAxisLabelProperties.m_fRotationAngleDegree);
100
101 const sal_Int32 nTextSize = bIsDirectionVertical ? aSizeAfterRotation.Height : aSizeAfterRotation.Width;
102
103 if( !nAvgCharWidth )
104 return;
105
106 static constexpr OUStringLiteral sDots = u"...";
107 const sal_Int32 nCharsToRemove = ( nTextSize - nMaxLabelsSize ) / nAvgCharWidth + 1;
108 sal_Int32 nNewLen = rLabel.size() - nCharsToRemove - sDots.getLength();
109 // Prevent from showing only dots
110 if (nNewLen < 0)
111 nNewLen = ( sal_Int32(rLabel.size()) >= sDots.getLength() ) ? sDots.getLength() : rLabel.size();
112
113 bool bCrop = nCharsToRemove > 0;
114 if( !bCrop )
115 return;
116
117 OUString aNewLabel( rLabel.substr( 0, nNewLen ) );
118 if( nNewLen > sDots.getLength() )
119 aNewLabel += sDots;
120 rShape2DText.setString( aNewLabel );
121
122 PropertyMapper::setMultiProperties( rPropNames, rPropValues, rShape2DText );
123}
124
127 , const awt::Point& rAnchorScreenPosition2D
128 , const OUString& rLabel
129 , const AxisLabelProperties& rAxisLabelProperties
130 , const AxisProperties& rAxisProperties
131 , const tNameSequence& rPropNames
132 , const tAnySequence& rPropValues
133 , const bool bIsHorizontalAxis
134 )
135{
136 if(rLabel.isEmpty())
137 return nullptr;
138
139 // #i78696# use mathematically correct rotation now
140 const double fRotationAnglePi(-basegfx::deg2rad(rAxisLabelProperties.m_fRotationAngleDegree));
141 uno::Any aATransformation = ShapeFactory::makeTransformation( rAnchorScreenPosition2D, fRotationAnglePi );
142 OUString aLabel = ShapeFactory::getStackedString( rLabel, rAxisLabelProperties.m_bStackCharacters );
143
144 rtl::Reference<SvxShapeText> xShape2DText =
145 ShapeFactory::createText( xTarget, aLabel, rPropNames, rPropValues, aATransformation );
146
147 if( rAxisProperties.m_bLimitSpaceForLabels )
148 lcl_ResizeTextShapeToFitAvailableSpace(*xShape2DText, rAxisLabelProperties, aLabel, rPropNames, rPropValues, bIsHorizontalAxis);
149
151 , rAxisProperties.maLabelAlignment.meAlignment, rAxisLabelProperties.m_fRotationAngleDegree, rAxisProperties.m_bComplexCategories );
152
153 return xShape2DText;
154}
155
157 , double fRotationAngleDegree
158 , const basegfx::B2DVector& rTickScreenPosition )
159{
160 ::basegfx::B2IRectangle aShapeRect = BaseGFXHelper::makeRectangle(rShape.getPosition(), ShapeFactory::getSizeAfterRotation( rShape, fRotationAngleDegree ));
161
162 basegfx::B2IVector aPosition(
163 static_cast<sal_Int32>( rTickScreenPosition.getX() )
164 , static_cast<sal_Int32>( rTickScreenPosition.getY() ) );
165 return aShapeRect.isInside(aPosition);
166}
167
168static void lcl_getRotatedPolygon( B2DPolygon &aPoly, const ::basegfx::B2DRectangle &aRect, const awt::Point &aPos, const double fRotationAngleDegree )
169{
171
172 // For rotating the rectangle we use the opposite angle,
173 // since `B2DHomMatrix` class used for
174 // representing the transformation, performs rotations in the positive
175 // direction (from the X axis to the Y axis). However since the coordinate
176 // system used by the chart has the Y-axis pointing downward, a rotation in
177 // the positive direction means a clockwise rotation. On the contrary text
178 // labels are rotated counterclockwise.
179 // The rotation is performed around the top-left vertex of the rectangle
180 // which is then moved to its final position by using the top-left
181 // vertex of the text label bounding box (aPos) as the translation vector.
183 aMatrix.rotate(-basegfx::deg2rad(fRotationAngleDegree));
184 aMatrix.translate( aPos.X, aPos.Y);
185 aPoly.transform( aMatrix );
186}
187
188static bool doesOverlap( const rtl::Reference<SvxShapeText>& xShape1
189 , const rtl::Reference<SvxShapeText>& xShape2
190 , double fRotationAngleDegree )
191{
192 if( !xShape1.is() || !xShape2.is() )
193 return false;
194
195 ::basegfx::B2DRectangle aRect1( BaseGFXHelper::makeRectangle( awt::Point(0,0), xShape1->getSize()));
196 ::basegfx::B2DRectangle aRect2( BaseGFXHelper::makeRectangle( awt::Point(0,0), xShape2->getSize()));
197
198 B2DPolygon aPoly1;
199 B2DPolygon aPoly2;
200 lcl_getRotatedPolygon( aPoly1, aRect1, xShape1->getPosition(), fRotationAngleDegree );
201 lcl_getRotatedPolygon( aPoly2, aRect2, xShape2->getPosition(), fRotationAngleDegree );
202
203 B2DPolyPolygon aPolyPoly1, aPolyPoly2;
204 aPolyPoly1.append( aPoly1 );
205 aPolyPoly2.append( aPoly2 );
206 B2DPolyPolygon overlapPoly = ::basegfx::utils::clipPolyPolygonOnPolyPolygon( aPolyPoly1, aPolyPoly2, true, false );
207
208 return (overlapPoly.count() > 0);
209}
210
212 , sal_Int32 nCorrectRhythm
213 , sal_Int32 nMaxTickToCheck
214 , const rtl::Reference< SvxShapeGroupAnyD >& xTarget )
215{
216 sal_Int32 nTick = 0;
217 for( TickInfo* pTickInfo = rIter.firstInfo()
218 ; pTickInfo && nTick <= nMaxTickToCheck
219 ; pTickInfo = rIter.nextInfo(), nTick++ )
220 {
221 //remove labels which does not fit into the rhythm
222 if( nTick%nCorrectRhythm != 0)
223 {
224 if(pTickInfo->xTextShape.is())
225 {
226 xTarget->remove(pTickInfo->xTextShape);
227 pTickInfo->xTextShape = nullptr;
228 }
229 }
230 }
231}
232
233namespace {
234
244class LabelIterator : public TickIter
245{
246public:
247 LabelIterator( TickInfoArrayType& rTickInfoVector
248 , const AxisLabelStaggering eAxisLabelStaggering
249 , bool bInnerLine );
250
251 virtual TickInfo* firstInfo() override;
252 virtual TickInfo* nextInfo() override;
253
254private: //member
255 PureTickIter m_aPureTickIter;
258};
259
260}
261
262LabelIterator::LabelIterator( TickInfoArrayType& rTickInfoVector
263 , const AxisLabelStaggering eAxisLabelStaggering
264 , bool bInnerLine )
265 : m_aPureTickIter( rTickInfoVector )
266 , m_eAxisLabelStaggering(eAxisLabelStaggering)
267 , m_bInnerLine(bInnerLine)
268{
269}
270
271TickInfo* LabelIterator::firstInfo()
272{
273 TickInfo* pTickInfo = m_aPureTickIter.firstInfo();
274 while( pTickInfo && !pTickInfo->xTextShape.is() )
275 pTickInfo = m_aPureTickIter.nextInfo();
276 if(!pTickInfo)
277 return nullptr;
278 if( (m_eAxisLabelStaggering==AxisLabelStaggering::StaggerEven && m_bInnerLine)
279 ||
280 (m_eAxisLabelStaggering==AxisLabelStaggering::StaggerOdd && !m_bInnerLine)
281 )
282 {
283 //skip first label
284 do
285 pTickInfo = m_aPureTickIter.nextInfo();
286 while( pTickInfo && !pTickInfo->xTextShape.is() );
287 }
288 if(!pTickInfo)
289 return nullptr;
290 return pTickInfo;
291}
292
293TickInfo* LabelIterator::nextInfo()
294{
295 TickInfo* pTickInfo = nullptr;
296 //get next label
297 do
298 pTickInfo = m_aPureTickIter.nextInfo();
299 while( pTickInfo && !pTickInfo->xTextShape.is() );
300
301 if( m_eAxisLabelStaggering==AxisLabelStaggering::StaggerEven
302 || m_eAxisLabelStaggering==AxisLabelStaggering::StaggerOdd )
303 {
304 //skip one label
305 do
306 pTickInfo = m_aPureTickIter.nextInfo();
307 while( pTickInfo && !pTickInfo->xTextShape.is() );
308 }
309 return pTickInfo;
310}
311
312static B2DVector lcl_getLabelsDistance( TickIter& rIter, const B2DVector& rDistanceTickToText, double fRotationAngleDegree )
313{
314 //calculates the height or width of a line of labels
315 //thus a following line of labels can be shifted for that distance
316
317 B2DVector aRet(0,0);
318
319 sal_Int32 nDistanceTickToText = static_cast<sal_Int32>( rDistanceTickToText.getLength() );
320 if( nDistanceTickToText==0.0)
321 return aRet;
322
323 B2DVector aStaggerDirection(rDistanceTickToText);
324 aStaggerDirection.normalize();
325
326 sal_Int32 nDistance=0;
328 for( TickInfo* pTickInfo = rIter.firstInfo()
329 ; pTickInfo
330 ; pTickInfo = rIter.nextInfo() )
331 {
332 xShape2DText = pTickInfo->xTextShape;
333 if( xShape2DText.is() )
334 {
335 awt::Size aSize = ShapeFactory::getSizeAfterRotation( *xShape2DText, fRotationAngleDegree );
336 if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY()))
337 nDistance = std::max(nDistance,aSize.Width);
338 else
339 nDistance = std::max(nDistance,aSize.Height);
340 }
341 }
342
343 aRet = aStaggerDirection*nDistance;
344
345 //add extra distance for vertical distance
346 if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY()))
347 aRet += rDistanceTickToText;
348
349 return aRet;
350}
351
352static void lcl_shiftLabels( TickIter& rIter, const B2DVector& rStaggerDistance )
353{
354 if(rStaggerDistance.getLength()==0.0)
355 return;
356 for( TickInfo* pTickInfo = rIter.firstInfo()
357 ; pTickInfo
358 ; pTickInfo = rIter.nextInfo() )
359 {
360 const rtl::Reference<SvxShapeText>& xShape2DText = pTickInfo->xTextShape;
361 if( xShape2DText.is() )
362 {
363 awt::Point aPos = xShape2DText->getPosition();
364 aPos.X += static_cast<sal_Int32>(rStaggerDistance.getX());
365 aPos.Y += static_cast<sal_Int32>(rStaggerDistance.getY());
366 xShape2DText->setPosition( aPos );
367 }
368 }
369}
370
372{
373 if (!xShape.is())
374 return false;
375
376 SvxTextEditSource* pTextEditSource = dynamic_cast<SvxTextEditSource*>(xShape->GetEditSource());
377 if (!pTextEditSource)
378 return false;
379
380 pTextEditSource->UpdateOutliner();
381 SvxTextForwarder* pTextForwarder = pTextEditSource->GetTextForwarder();
382 if (!pTextForwarder)
383 return false;
384
385 sal_Int32 nParaCount = pTextForwarder->GetParagraphCount();
386 for ( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
387 {
388 sal_Int32 nLineCount = pTextForwarder->GetLineCount( nPara );
389 for ( sal_Int32 nLine = 0; nLine < nLineCount; ++nLine )
390 {
391 sal_Int32 nLineStart = 0;
392 sal_Int32 nLineEnd = 0;
393 pTextForwarder->GetLineBoundaries( nLineStart, nLineEnd, nPara, nLine );
394 assert(nLineStart >= 0);
395 sal_Int32 nWordStart = 0;
396 sal_Int32 nWordEnd = 0;
397 if ( pTextForwarder->GetWordIndices( nPara, nLineStart, nWordStart, nWordEnd ) &&
398 ( nWordStart != nLineStart ) )
399 {
400 return true;
401 }
402 }
403 }
404
405 return false;
406}
407
408static OUString getTextLabelString(
409 const FixedNumberFormatter& rFixedNumberFormatter, const uno::Sequence<OUString>* pCategories,
410 const TickInfo* pTickInfo, bool bComplexCat, Color& rExtraColor, bool& rHasExtraColor )
411{
412 if (pCategories)
413 {
414 // This is a normal category axis. Get the label string from the
415 // label string array.
416 sal_Int32 nIndex = static_cast<sal_Int32>(pTickInfo->getUnscaledTickValue()) - 1; //first category (index 0) matches with real number 1.0
417 if( nIndex>=0 && nIndex<pCategories->getLength() )
418 return (*pCategories)[nIndex];
419
420 return OUString();
421 }
422 else if (bComplexCat)
423 {
424 // This is a complex category axis. The label is stored in the tick.
425 return pTickInfo->aText;
426 }
427
428 // This is a numeric axis. Format the original tick value per number format.
429 return rFixedNumberFormatter.getFormattedString(pTickInfo->getUnscaledTickValue(), rExtraColor, rHasExtraColor);
430}
431
433 tNameSequence& rPropNames, tAnySequence& rPropValues, const AxisProperties& rAxisProp,
434 const AxisLabelProperties& rAxisLabelProp,
435 sal_Int32 nLimitedSpaceForText, bool bLimitedHeight )
436{
438
439 PropertyMapper::getTextLabelMultiPropertyLists(
440 xProps, rPropNames, rPropValues, false, nLimitedSpaceForText, bLimitedHeight, false);
441
442 LabelPositionHelper::doDynamicFontResize(
443 rPropValues, rPropNames, xProps, rAxisLabelProp.m_aFontReferenceSize);
444
445 LabelPositionHelper::changeTextAdjustment(
446 rPropValues, rPropNames, rAxisProp.maLabelAlignment.meAlignment);
447}
448
449namespace {
450
457class MaxLabelTickIter : public TickIter
458{
459public:
460 MaxLabelTickIter( TickInfoArrayType& rTickInfoVector, size_t nLongestLabelIndex );
461
462 virtual TickInfo* firstInfo() override;
463 virtual TickInfo* nextInfo() override;
464
465private:
467 std::vector<size_t> m_aValidIndices;
469};
470
471}
472
473MaxLabelTickIter::MaxLabelTickIter(
474 TickInfoArrayType& rTickInfoVector, size_t nLongestLabelIndex ) :
475 m_rTickInfoVector(rTickInfoVector), m_nCurrentIndex(0)
476{
477 assert(!rTickInfoVector.empty()); // should be checked by the caller.
478 assert(nLongestLabelIndex < rTickInfoVector.size());
479
480 size_t nMaxIndex = m_rTickInfoVector.size()-1;
481 if (nLongestLabelIndex >= nMaxIndex-1)
482 nLongestLabelIndex = 0;
483
484 if (nLongestLabelIndex > 0)
485 m_aValidIndices.push_back(nLongestLabelIndex-1);
486
487 m_aValidIndices.push_back(nLongestLabelIndex);
488
489 while (m_aValidIndices.size() < 3)
490 {
491 ++nLongestLabelIndex;
492 if (nLongestLabelIndex > nMaxIndex)
493 break;
494
495 m_aValidIndices.push_back(nLongestLabelIndex);
496 }
497}
498
499TickInfo* MaxLabelTickIter::firstInfo()
500{
501 m_nCurrentIndex = 0;
502 if (m_nCurrentIndex < m_aValidIndices.size())
504 return nullptr;
505}
506
507TickInfo* MaxLabelTickIter::nextInfo()
508{
510 if (m_nCurrentIndex < m_aValidIndices.size())
512 return nullptr;
513}
514
515bool VCartesianAxis::isBreakOfLabelsAllowed(
516 const AxisLabelProperties& rAxisLabelProperties, bool bIsHorizontalAxis, bool bIsVerticalAxis) const
517{
518 if( m_aTextLabels.getLength() > 100 )
519 return false;
520 if( !rAxisLabelProperties.m_bLineBreakAllowed )
521 return false;
522 if( rAxisLabelProperties.m_bStackCharacters )
523 return false;
524 //no break for value axis
525 if( !m_bUseTextLabels )
526 return false;
527 if( !( rAxisLabelProperties.m_fRotationAngleDegree == 0.0 ||
528 rAxisLabelProperties.m_fRotationAngleDegree == 90.0 ||
529 rAxisLabelProperties.m_fRotationAngleDegree == 270.0 ) )
530 return false;
531 //no break for complex vertical category axis
532 if( !m_aAxisProperties.m_bSwapXAndY )
533 return bIsHorizontalAxis;
534 else if( m_aAxisProperties.m_bSwapXAndY && !m_aAxisProperties.m_bComplexCategories )
535 return bIsVerticalAxis;
536 else
537 return false;
538}
539namespace{
540
541bool canAutoAdjustLabelPlacement(
542 const AxisLabelProperties& rAxisLabelProperties, bool bIsHorizontalAxis, bool bIsVerticalAxis)
543{
544 // joined prerequisite checks for auto rotate and auto stagger
545 if( rAxisLabelProperties.m_bOverlapAllowed )
546 return false;
547 if( rAxisLabelProperties.m_bLineBreakAllowed ) // auto line break may conflict with...
548 return false;
549 if( rAxisLabelProperties.m_fRotationAngleDegree != 0.0 )
550 return false;
551 // automatic adjusting labels only works for
552 // horizontal axis with horizontal text
553 // or vertical axis with vertical text
554 if( bIsHorizontalAxis )
555 return !rAxisLabelProperties.m_bStackCharacters;
556 if( bIsVerticalAxis )
557 return rAxisLabelProperties.m_bStackCharacters;
558 return false;
559}
560
561bool isAutoStaggeringOfLabelsAllowed(
562 const AxisLabelProperties& rAxisLabelProperties, bool bIsHorizontalAxis, bool bIsVerticalAxis )
563{
564 if( rAxisLabelProperties.m_eStaggering != AxisLabelStaggering::StaggerAuto )
565 return false;
566 return canAutoAdjustLabelPlacement(rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis);
567}
568
569// make clear that we check for auto rotation prerequisites
570const auto& isAutoRotatingOfLabelsAllowed = canAutoAdjustLabelPlacement;
571
572} // namespace
573void VCartesianAxis::createAllTickInfosFromComplexCategories( TickInfoArraysType& rAllTickInfos, bool bShiftedPosition )
574{
575 //no minor tickmarks will be generated!
576 //order is: inner labels first , outer labels last (that is different to all other TickIter cases)
577 if(!bShiftedPosition)
578 {
579 rAllTickInfos.clear();
580 sal_Int32 nLevel=0;
581 sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount();
582 for( ; nLevel<nLevelCount; nLevel++ )
583 {
584 TickInfoArrayType aTickInfoVector;
585 const std::vector<ComplexCategory>* pComplexCategories =
586 m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel(nLevel);
587
588 if (!pComplexCategories)
589 continue;
590
591 sal_Int32 nCatIndex = 0;
592
593 for (auto const& complexCategory : *pComplexCategories)
594 {
595 TickInfo aTickInfo(nullptr);
596 sal_Int32 nCount = complexCategory.Count;
597 if( nCatIndex + 1.0 + nCount >= m_aScale.Maximum )
598 {
599 nCount = static_cast<sal_Int32>(m_aScale.Maximum - 1.0 - nCatIndex);
600 if( nCount <= 0 )
601 nCount = 1;
602 }
603 aTickInfo.fScaledTickValue = nCatIndex + 1.0 + nCount/2.0;
605 aTickInfo.aText = complexCategory.Text;
606 aTickInfoVector.push_back(aTickInfo);
607 nCatIndex += nCount;
608 if( nCatIndex + 1.0 >= m_aScale.Maximum )
609 break;
610 }
611 rAllTickInfos.push_back(aTickInfoVector);
612 }
613 }
614 else //bShiftedPosition==false
615 {
616 rAllTickInfos.clear();
617 sal_Int32 nLevel=0;
618 sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount();
619 for( ; nLevel<nLevelCount; nLevel++ )
620 {
621 TickInfoArrayType aTickInfoVector;
622 const std::vector<ComplexCategory>* pComplexCategories =
623 m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel(nLevel);
624 sal_Int32 nCatIndex = 0;
625 if (pComplexCategories)
626 {
627 for (auto const& complexCategory : *pComplexCategories)
628 {
629 TickInfo aTickInfo(nullptr);
630 aTickInfo.fScaledTickValue = nCatIndex + 1.0;
631 aTickInfoVector.push_back(aTickInfo);
632 nCatIndex += complexCategory.Count;
633 if( nCatIndex + 1.0 > m_aScale.Maximum )
634 break;
635 }
636 }
637
638 //fill up with single ticks until maximum scale
639 while( nCatIndex + 1.0 < m_aScale.Maximum )
640 {
641 TickInfo aTickInfo(nullptr);
642 aTickInfo.fScaledTickValue = nCatIndex + 1.0;
643 aTickInfoVector.push_back(aTickInfo);
644 nCatIndex ++;
645 if( nLevel>0 )
646 break;
647 }
648 //add an additional tick at the end
649 {
650 TickInfo aTickInfo(nullptr);
651 aTickInfo.fScaledTickValue = m_aScale.Maximum;
652 aTickInfoVector.push_back(aTickInfo);
653 }
654 rAllTickInfos.push_back(aTickInfoVector);
655 }
656 }
657}
658
659void VCartesianAxis::createAllTickInfos( TickInfoArraysType& rAllTickInfos )
660{
661 if( isComplexCategoryAxis() )
662 createAllTickInfosFromComplexCategories( rAllTickInfos, false );
663 else
664 VAxisBase::createAllTickInfos(rAllTickInfos);
665}
666
667TickIter* VCartesianAxis::createLabelTickIterator( sal_Int32 nTextLevel )
668{
669 if( nTextLevel>=0 && o3tl::make_unsigned(nTextLevel) < m_aAllTickInfos.size() )
670 return new PureTickIter( m_aAllTickInfos[nTextLevel] );
671 return nullptr;
672}
673
674TickIter* VCartesianAxis::createMaximumLabelTickIterator( sal_Int32 nTextLevel )
675{
676 if( isComplexCategoryAxis() || isDateAxis() )
677 {
678 return createLabelTickIterator( nTextLevel ); //mmmm maybe todo: create less than all texts here
679 }
680 else
681 {
682 if(nTextLevel==0)
683 {
684 if( !m_aAllTickInfos.empty() )
685 {
686 size_t nLongestLabelIndex = m_bUseTextLabels ? getIndexOfLongestLabel(m_aTextLabels) : 0;
687 if (nLongestLabelIndex >= m_aAllTickInfos[0].size())
688 return nullptr;
689
690 return new MaxLabelTickIter( m_aAllTickInfos[0], nLongestLabelIndex );
691 }
692 }
693 }
694 return nullptr;
695}
696
697sal_Int32 VCartesianAxis::getTextLevelCount() const
698{
699 sal_Int32 nTextLevelCount = 1;
700 if( isComplexCategoryAxis() )
701 nTextLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount();
702 return nTextLevelCount;
703}
704
705bool VCartesianAxis::createTextShapes(
706 const rtl::Reference< SvxShapeGroupAnyD >& xTarget, TickIter& rTickIter,
707 AxisLabelProperties& rAxisLabelProperties, TickFactory2D const * pTickFactory,
708 sal_Int32 nScreenDistanceBetweenTicks )
709{
710 const bool bIsHorizontalAxis = pTickFactory->isHorizontalAxis();
711 const bool bIsVerticalAxis = pTickFactory->isVerticalAxis();
712
713 if( m_bUseTextLabels && (m_aAxisProperties.m_eLabelPos == css::chart::ChartAxisLabelPosition_NEAR_AXIS ||
714 m_aAxisProperties.m_eLabelPos == css::chart::ChartAxisLabelPosition_OUTSIDE_START))
715 {
716 if (bIsHorizontalAxis)
717 {
718 rAxisLabelProperties.m_aMaximumSpaceForLabels.Y = pTickFactory->getXaxisStartPos().getY();
719 rAxisLabelProperties.m_aMaximumSpaceForLabels.Height = rAxisLabelProperties.m_aFontReferenceSize.Height - rAxisLabelProperties.m_aMaximumSpaceForLabels.Y;
720 }
721 else if (bIsVerticalAxis)
722 {
723 rAxisLabelProperties.m_aMaximumSpaceForLabels.X = 0;
724 rAxisLabelProperties.m_aMaximumSpaceForLabels.Width = pTickFactory->getXaxisStartPos().getX();
725 }
726 }
727
728 bool bIsBreakOfLabelsAllowed = isBreakOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis );
729 if (!bIsBreakOfLabelsAllowed &&
730 !isAutoStaggeringOfLabelsAllowed(rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis) &&
731 !rAxisLabelProperties.isStaggered())
732 {
733 return createTextShapesSimple(xTarget, rTickIter, rAxisLabelProperties, pTickFactory);
734 }
735
736 FixedNumberFormatter aFixedNumberFormatter(
737 m_xNumberFormatsSupplier, rAxisLabelProperties.m_nNumberFormatKey );
738
739 bool bIsStaggered = rAxisLabelProperties.isStaggered();
740 B2DVector aTextToTickDistance = pTickFactory->getDistanceAxisTickToText(m_aAxisProperties, true);
741 sal_Int32 nLimitedSpaceForText = -1;
742
743 if (bIsBreakOfLabelsAllowed)
744 {
745 if (!m_aAxisProperties.m_bLimitSpaceForLabels)
746 {
747 basegfx::B2DVector nDeltaVector = pTickFactory->getXaxisEndPos() - pTickFactory->getXaxisStartPos();
748 nLimitedSpaceForText = nDeltaVector.getX();
749 }
750 if (nScreenDistanceBetweenTicks > 0)
751 nLimitedSpaceForText = nScreenDistanceBetweenTicks;
752
753 if( bIsStaggered )
754 nLimitedSpaceForText *= 2;
755
756 if( nLimitedSpaceForText > 0 )
757 { //reduce space for a small amount to have a visible distance between the labels:
758 sal_Int32 nReduce = (nLimitedSpaceForText*5)/100;
759 if(!nReduce)
760 nReduce = 1;
761 nLimitedSpaceForText -= nReduce;
762 }
763
764 // recalculate the nLimitedSpaceForText in case of 90 and 270 degree if the text break is true
765 if ( rAxisLabelProperties.m_fRotationAngleDegree == 90.0 || rAxisLabelProperties.m_fRotationAngleDegree == 270.0 )
766 {
767 nLimitedSpaceForText = rAxisLabelProperties.m_aMaximumSpaceForLabels.Height;
768 m_aAxisProperties.m_bLimitSpaceForLabels = false;
769 }
770
771 // recalculate the nLimitedSpaceForText in case of vertical category axis if the text break is true
772 if ( m_aAxisProperties.m_bSwapXAndY && bIsVerticalAxis && rAxisLabelProperties.m_fRotationAngleDegree == 0.0 )
773 {
774 nLimitedSpaceForText = pTickFactory->getXaxisStartPos().getX();
775 m_aAxisProperties.m_bLimitSpaceForLabels = false;
776 }
777 }
778
779 // Stores an array of text label strings in case of a normal
780 // (non-complex) category axis.
781 const uno::Sequence<OUString>* pCategories = nullptr;
782 if( m_bUseTextLabels && !m_aAxisProperties.m_bComplexCategories )
783 pCategories = &m_aTextLabels;
784
785 bool bLimitedHeight;
786 if( !m_aAxisProperties.m_bSwapXAndY )
787 bLimitedHeight = fabs(aTextToTickDistance.getX()) > fabs(aTextToTickDistance.getY());
788 else
789 bLimitedHeight = fabs(aTextToTickDistance.getX()) < fabs(aTextToTickDistance.getY());
790 //prepare properties for multipropertyset-interface of shape
792 tAnySequence aPropValues;
793 getAxisLabelProperties(aPropNames, aPropValues, m_aAxisProperties, rAxisLabelProperties, nLimitedSpaceForText, bLimitedHeight);
794
795 uno::Any* pColorAny = PropertyMapper::getValuePointer(aPropValues,aPropNames,u"CharColor");
796 Color nColor = COL_AUTO;
797 if(pColorAny)
798 *pColorAny >>= nColor;
799
800 uno::Any* pLimitedSpaceAny = PropertyMapper::getValuePointerForLimitedSpace(aPropValues,aPropNames,bLimitedHeight);
801
802 const TickInfo* pPreviousVisibleTickInfo = nullptr;
803 const TickInfo* pPREPreviousVisibleTickInfo = nullptr;
804 sal_Int32 nTick = 0;
805 for( TickInfo* pTickInfo = rTickIter.firstInfo()
806 ; pTickInfo
807 ; pTickInfo = rTickIter.nextInfo(), nTick++ )
808 {
809 const TickInfo* pLastVisibleNeighbourTickInfo = bIsStaggered ?
810 pPREPreviousVisibleTickInfo : pPreviousVisibleTickInfo;
811
812 //don't create labels which does not fit into the rhythm
813 if( nTick%rAxisLabelProperties.m_nRhythm != 0 )
814 continue;
815
816 //don't create labels for invisible ticks
817 if( !pTickInfo->bPaintIt )
818 continue;
819
820 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.m_bOverlapAllowed )
821 {
822 // Overlapping is not allowed. If the label overlaps with its
823 // neighboring label, try increasing the tick interval (or rhythm
824 // as it's called) and start over.
825
826 if( lcl_doesShapeOverlapWithTickmark( *pLastVisibleNeighbourTickInfo->xTextShape
827 , rAxisLabelProperties.m_fRotationAngleDegree
828 , pTickInfo->aTickScreenPosition ) )
829 {
830 // This tick overlaps with its neighbor. Try to stagger (if
831 // auto staggering is allowed) to avoid overlapping.
832
833 bool bOverlapsAfterAutoStagger = true;
834 if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) )
835 {
836 bIsStaggered = true;
837 rAxisLabelProperties.m_eStaggering = AxisLabelStaggering::StaggerEven;
838 pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo;
839 if( !pLastVisibleNeighbourTickInfo ||
840 !lcl_doesShapeOverlapWithTickmark( *pLastVisibleNeighbourTickInfo->xTextShape
841 , rAxisLabelProperties.m_fRotationAngleDegree
842 , pTickInfo->aTickScreenPosition ) )
843 bOverlapsAfterAutoStagger = false;
844 }
845
846 if (bOverlapsAfterAutoStagger)
847 {
848 // Still overlaps with its neighbor even after staggering.
849 // Increment the visible tick intervals (if that's
850 // allowed) and start over.
851
852 rAxisLabelProperties.m_nRhythm++;
853 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.m_nRhythm, nTick, xTarget );
854 return false;
855 }
856 }
857 }
858
859 bool bHasExtraColor=false;
860 Color nExtraColor;
861
862 OUString aLabel = getTextLabelString(
863 aFixedNumberFormatter, pCategories, pTickInfo, isComplexCategoryAxis(),
864 nExtraColor, bHasExtraColor);
865
866 if(pColorAny)
867 *pColorAny <<= bHasExtraColor?nExtraColor:nColor;
868 if(pLimitedSpaceAny)
869 *pLimitedSpaceAny <<= sal_Int32(nLimitedSpaceForText*pTickInfo->nFactorForLimitedTextWidth);
870
871 B2DVector aTickScreenPos2D = pTickInfo->aTickScreenPosition;
872 aTickScreenPos2D += aTextToTickDistance;
873 awt::Point aAnchorScreenPosition2D(
874 static_cast<sal_Int32>(aTickScreenPos2D.getX())
875 ,static_cast<sal_Int32>(aTickScreenPos2D.getY()));
876
877 //create single label
878 if(!pTickInfo->xTextShape.is())
879 {
880 pTickInfo->xTextShape = createSingleLabel( xTarget
881 , aAnchorScreenPosition2D, aLabel
882 , rAxisLabelProperties, m_aAxisProperties
883 , aPropNames, aPropValues, bIsHorizontalAxis );
884 }
885 if(!pTickInfo->xTextShape.is())
886 continue;
887
888 recordMaximumTextSize( *pTickInfo->xTextShape, rAxisLabelProperties.m_fRotationAngleDegree );
889
890 // Label has multiple lines and the words are broken
891 if (nLimitedSpaceForText > 0
892 && !rAxisLabelProperties.m_bOverlapAllowed
893 && rAxisLabelProperties.m_fRotationAngleDegree == 0.0
894 && nTick > 0
895 && lcl_hasWordBreak(pTickInfo->xTextShape))
896 {
897 // Label has multiple lines and belongs to a complex category
898 // axis. Rotate 90 degrees to try to avoid overlaps.
899 if ( m_aAxisProperties.m_bComplexCategories )
900 {
901 rAxisLabelProperties.m_fRotationAngleDegree = 90;
902 }
903 rAxisLabelProperties.m_bLineBreakAllowed = false;
904 m_aAxisLabelProperties.m_fRotationAngleDegree = rAxisLabelProperties.m_fRotationAngleDegree;
905 removeTextShapesFromTicks();
906 return false;
907 }
908
909 //if NO OVERLAP -> remove overlapping shapes
910 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.m_bOverlapAllowed )
911 {
912 // Check if the label still overlaps with its neighbor.
913 if( doesOverlap( pLastVisibleNeighbourTickInfo->xTextShape, pTickInfo->xTextShape, rAxisLabelProperties.m_fRotationAngleDegree ) )
914 {
915 // It overlaps. Check if staggering helps.
916 bool bOverlapsAfterAutoStagger = true;
917 if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) )
918 {
919 // Compatibility option: starting from LibreOffice 5.1 the rotated
920 // layout is preferred to staggering for axis labels.
921 if( !isAutoRotatingOfLabelsAllowed(rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis)
922 || m_aAxisProperties.m_bTryStaggeringFirst )
923 {
924 bIsStaggered = true;
925 rAxisLabelProperties.m_eStaggering = AxisLabelStaggering::StaggerEven;
926 pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo;
927 if( !pLastVisibleNeighbourTickInfo ||
928 !lcl_doesShapeOverlapWithTickmark( *pLastVisibleNeighbourTickInfo->xTextShape
929 , rAxisLabelProperties.m_fRotationAngleDegree
930 , pTickInfo->aTickScreenPosition ) )
931 bOverlapsAfterAutoStagger = false;
932 }
933 }
934
935 if (bOverlapsAfterAutoStagger)
936 {
937 // Staggering didn't solve the overlap.
938 if( isAutoRotatingOfLabelsAllowed(rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis) )
939 {
940 // Try auto-rotating the labels at 45 degrees and
941 // start over. This rotation angle will be stored for
942 // all future text shape creation runs.
943 // The nRhythm parameter is reset to 1 since the layout
944 // used for text labels is changed.
945 rAxisLabelProperties.autoRotate45();
946 m_aAxisLabelProperties.m_fRotationAngleDegree = rAxisLabelProperties.m_fRotationAngleDegree; // Store it for future runs.
947 removeTextShapesFromTicks();
948 rAxisLabelProperties.m_nRhythm = 1;
949 return false;
950 }
951
952 // Try incrementing the tick interval and start over.
953 rAxisLabelProperties.m_nRhythm++;
954 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.m_nRhythm, nTick, xTarget );
955 return false;
956 }
957 }
958 }
959
960 pPREPreviousVisibleTickInfo = pPreviousVisibleTickInfo;
961 pPreviousVisibleTickInfo = pTickInfo;
962 }
963 return true;
964}
965
966bool VCartesianAxis::createTextShapesSimple(
967 const rtl::Reference< SvxShapeGroupAnyD >& xTarget, TickIter& rTickIter,
968 AxisLabelProperties& rAxisLabelProperties, TickFactory2D const * pTickFactory )
969{
970 FixedNumberFormatter aFixedNumberFormatter(
971 m_xNumberFormatsSupplier, rAxisLabelProperties.m_nNumberFormatKey );
972
973 const bool bIsHorizontalAxis = pTickFactory->isHorizontalAxis();
974 const bool bIsVerticalAxis = pTickFactory->isVerticalAxis();
975 B2DVector aTextToTickDistance = pTickFactory->getDistanceAxisTickToText(m_aAxisProperties, true);
976
977 // Stores an array of text label strings in case of a normal
978 // (non-complex) category axis.
979 const uno::Sequence<OUString>* pCategories = nullptr;
980 if( m_bUseTextLabels && !m_aAxisProperties.m_bComplexCategories )
981 pCategories = &m_aTextLabels;
982
983 bool bLimitedHeight = fabs(aTextToTickDistance.getX()) > fabs(aTextToTickDistance.getY());
984
985 //prepare properties for multipropertyset-interface of shape
987 tAnySequence aPropValues;
988 getAxisLabelProperties(aPropNames, aPropValues, m_aAxisProperties, rAxisLabelProperties, -1, bLimitedHeight);
989
990 uno::Any* pColorAny = PropertyMapper::getValuePointer(aPropValues,aPropNames,u"CharColor");
991 Color nColor = COL_AUTO;
992 if(pColorAny)
993 *pColorAny >>= nColor;
994
995 uno::Any* pLimitedSpaceAny = PropertyMapper::getValuePointerForLimitedSpace(aPropValues,aPropNames,bLimitedHeight);
996
997 const TickInfo* pPreviousVisibleTickInfo = nullptr;
998 sal_Int32 nTick = 0;
999 for( TickInfo* pTickInfo = rTickIter.firstInfo()
1000 ; pTickInfo
1001 ; pTickInfo = rTickIter.nextInfo(), nTick++ )
1002 {
1003 const TickInfo* pLastVisibleNeighbourTickInfo = pPreviousVisibleTickInfo;
1004
1005 //don't create labels which does not fit into the rhythm
1006 if( nTick%rAxisLabelProperties.m_nRhythm != 0 )
1007 continue;
1008
1009 //don't create labels for invisible ticks
1010 if( !pTickInfo->bPaintIt )
1011 continue;
1012
1013 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.m_bOverlapAllowed )
1014 {
1015 // Overlapping is not allowed. If the label overlaps with its
1016 // neighboring label, try increasing the tick interval (or rhythm
1017 // as it's called) and start over.
1018
1019 if( lcl_doesShapeOverlapWithTickmark( *pLastVisibleNeighbourTickInfo->xTextShape
1020 , rAxisLabelProperties.m_fRotationAngleDegree
1021 , pTickInfo->aTickScreenPosition ) )
1022 {
1023 // This tick overlaps with its neighbor. Increment the visible
1024 // tick intervals (if that's allowed) and start over.
1025
1026 rAxisLabelProperties.m_nRhythm++;
1027 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.m_nRhythm, nTick, xTarget );
1028 return false;
1029 }
1030 }
1031
1032 bool bHasExtraColor=false;
1033 Color nExtraColor;
1034
1035 OUString aLabel = getTextLabelString(
1036 aFixedNumberFormatter, pCategories, pTickInfo, isComplexCategoryAxis(),
1037 nExtraColor, bHasExtraColor);
1038
1039 if(pColorAny)
1040 *pColorAny <<= bHasExtraColor?nExtraColor:nColor;
1041 if(pLimitedSpaceAny)
1042 *pLimitedSpaceAny <<= sal_Int32(-1*pTickInfo->nFactorForLimitedTextWidth);
1043
1044 B2DVector aTickScreenPos2D = pTickInfo->aTickScreenPosition;
1045 aTickScreenPos2D += aTextToTickDistance;
1046 awt::Point aAnchorScreenPosition2D(
1047 static_cast<sal_Int32>(aTickScreenPos2D.getX())
1048 ,static_cast<sal_Int32>(aTickScreenPos2D.getY()));
1049
1050 //create single label
1051 if(!pTickInfo->xTextShape.is())
1052 pTickInfo->xTextShape = createSingleLabel( xTarget
1053 , aAnchorScreenPosition2D, aLabel
1054 , rAxisLabelProperties, m_aAxisProperties
1055 , aPropNames, aPropValues, bIsHorizontalAxis );
1056 if(!pTickInfo->xTextShape.is())
1057 continue;
1058
1059 recordMaximumTextSize( *pTickInfo->xTextShape, rAxisLabelProperties.m_fRotationAngleDegree );
1060
1061 //if NO OVERLAP -> remove overlapping shapes
1062 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.m_bOverlapAllowed )
1063 {
1064 // Check if the label still overlaps with its neighbor.
1065 if( doesOverlap( pLastVisibleNeighbourTickInfo->xTextShape, pTickInfo->xTextShape, rAxisLabelProperties.m_fRotationAngleDegree ) )
1066 {
1067 // It overlaps.
1068 if( isAutoRotatingOfLabelsAllowed(rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis) )
1069 {
1070 // Try auto-rotating the labels at 45 degrees and
1071 // start over. This rotation angle will be stored for
1072 // all future text shape creation runs.
1073 // The nRhythm parameter is reset to 1 since the layout
1074 // used for text labels is changed.
1075 rAxisLabelProperties.autoRotate45();
1076 m_aAxisLabelProperties.m_fRotationAngleDegree = rAxisLabelProperties.m_fRotationAngleDegree; // Store it for future runs.
1077 removeTextShapesFromTicks();
1078 rAxisLabelProperties.m_nRhythm = 1;
1079 return false;
1080 }
1081
1082 // Try incrementing the tick interval and start over.
1083 rAxisLabelProperties.m_nRhythm++;
1084 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.m_nRhythm, nTick, xTarget );
1085 return false;
1086 }
1087 }
1088
1089 pPreviousVisibleTickInfo = pTickInfo;
1090 }
1091 return true;
1092}
1093
1094double VCartesianAxis::getAxisIntersectionValue() const
1095{
1096 if (m_aAxisProperties.m_pfMainLinePositionAtOtherAxis)
1097 return *m_aAxisProperties.m_pfMainLinePositionAtOtherAxis;
1098
1099 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY();
1100 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY();
1101
1102 return (m_aAxisProperties.m_eCrossoverType == css::chart::ChartAxisPosition_END) ? fMax : fMin;
1103}
1104
1105double VCartesianAxis::getLabelLineIntersectionValue() const
1106{
1107 if (m_aAxisProperties.m_eLabelPos == css::chart::ChartAxisLabelPosition_OUTSIDE_START)
1108 return (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY();
1109
1110 if (m_aAxisProperties.m_eLabelPos == css::chart::ChartAxisLabelPosition_OUTSIDE_END)
1111 return (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY();
1112
1113 return getAxisIntersectionValue();
1114}
1115
1116double VCartesianAxis::getExtraLineIntersectionValue() const
1117{
1118 if( !m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis )
1119 return std::numeric_limits<double>::quiet_NaN();
1120
1121 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY();
1122 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY();
1123
1124 if( *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis <= fMin
1125 || *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis >= fMax )
1126 return std::numeric_limits<double>::quiet_NaN();
1127
1128 return *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis;
1129}
1130
1131B2DVector VCartesianAxis::getScreenPosition( double fLogicX, double fLogicY, double fLogicZ ) const
1132{
1133 B2DVector aRet(0,0);
1134
1135 if( m_pPosHelper )
1136 {
1137 drawing::Position3D aScenePos = m_pPosHelper->transformLogicToScene( fLogicX, fLogicY, fLogicZ, true );
1138 if(m_nDimension==3)
1139 {
1140 if (m_xLogicTarget.is())
1141 {
1142 tPropertyNameMap aDummyPropertyNameMap;
1143 rtl::Reference<Svx3DExtrudeObject> xShape3DAnchor = ShapeFactory::createCube( m_xLogicTarget
1144 , aScenePos,drawing::Direction3D(1,1,1), 0, nullptr, aDummyPropertyNameMap);
1145 awt::Point a2DPos = xShape3DAnchor->getPosition(); //get 2D position from xShape3DAnchor
1146 m_xLogicTarget->remove(xShape3DAnchor);
1147 aRet.setX( a2DPos.X );
1148 aRet.setY( a2DPos.Y );
1149 }
1150 else
1151 {
1152 OSL_FAIL("cannot calculate screen position in VCartesianAxis::getScreenPosition");
1153 }
1154 }
1155 else
1156 {
1157 aRet.setX( aScenePos.PositionX );
1158 aRet.setY( aScenePos.PositionY );
1159 }
1160 }
1161
1162 return aRet;
1163}
1164
1165VCartesianAxis::ScreenPosAndLogicPos VCartesianAxis::getScreenPosAndLogicPos( double fLogicX_, double fLogicY_, double fLogicZ_ ) const
1166{
1168 aRet.fLogicX = fLogicX_;
1169 aRet.fLogicY = fLogicY_;
1170 aRet.fLogicZ = fLogicZ_;
1171 aRet.aScreenPos = getScreenPosition( fLogicX_, fLogicY_, fLogicZ_ );
1172 return aRet;
1173}
1174
1175typedef std::vector< VCartesianAxis::ScreenPosAndLogicPos > tScreenPosAndLogicPosList;
1176
1177namespace {
1178
1179struct lcl_LessXPos
1180{
1181 bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 )
1182 {
1183 return ( rPos1.aScreenPos.getX() < rPos2.aScreenPos.getX() );
1184 }
1185};
1186
1187struct lcl_GreaterYPos
1188{
1189 bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 )
1190 {
1191 return ( rPos1.aScreenPos.getY() > rPos2.aScreenPos.getY() );
1192 }
1193};
1194
1195}
1196
1197void VCartesianAxis::get2DAxisMainLine(
1198 B2DVector& rStart, B2DVector& rEnd, AxisLabelAlignment& rAlignment, double fCrossesOtherAxis ) const
1199{
1200 //m_aAxisProperties might get updated and changed here because
1201 // the label alignment and inner direction sign depends exactly of the choice of the axis line position which is made here in this method
1202
1203 double const fMinX = m_pPosHelper->getLogicMinX();
1204 double const fMinY = m_pPosHelper->getLogicMinY();
1205 double const fMinZ = m_pPosHelper->getLogicMinZ();
1206 double const fMaxX = m_pPosHelper->getLogicMaxX();
1207 double const fMaxY = m_pPosHelper->getLogicMaxY();
1208 double const fMaxZ = m_pPosHelper->getLogicMaxZ();
1209
1210 double fXOnXPlane = fMinX;
1211 double fXOther = fMaxX;
1212 int nDifferentValue = !m_pPosHelper->isMathematicalOrientationX() ? -1 : 1;
1213 if( !m_pPosHelper->isSwapXAndY() )
1214 nDifferentValue *= (m_eLeftWallPos != CuboidPlanePosition_Left) ? -1 : 1;
1215 else
1216 nDifferentValue *= (m_eBottomPos != CuboidPlanePosition_Bottom) ? -1 : 1;
1217 if( nDifferentValue<0 )
1218 {
1219 fXOnXPlane = fMaxX;
1220 fXOther = fMinX;
1221 }
1222
1223 double fYOnYPlane = fMinY;
1224 double fYOther = fMaxY;
1225 nDifferentValue = !m_pPosHelper->isMathematicalOrientationY() ? -1 : 1;
1226 if( !m_pPosHelper->isSwapXAndY() )
1227 nDifferentValue *= (m_eBottomPos != CuboidPlanePosition_Bottom) ? -1 : 1;
1228 else
1229 nDifferentValue *= (m_eLeftWallPos != CuboidPlanePosition_Left) ? -1 : 1;
1230 if( nDifferentValue<0 )
1231 {
1232 fYOnYPlane = fMaxY;
1233 fYOther = fMinY;
1234 }
1235
1236 double fZOnZPlane = fMaxZ;
1237 double fZOther = fMinZ;
1238 nDifferentValue = !m_pPosHelper->isMathematicalOrientationZ() ? -1 : 1;
1239 nDifferentValue *= (m_eBackWallPos != CuboidPlanePosition_Back) ? -1 : 1;
1240 if( nDifferentValue<0 )
1241 {
1242 fZOnZPlane = fMinZ;
1243 fZOther = fMaxZ;
1244 }
1245
1246 double fXStart = fMinX;
1247 double fYStart = fMinY;
1248 double fZStart = fMinZ;
1249 double fXEnd;
1250 double fYEnd;
1251 double fZEnd = fZStart;
1252
1253 if( m_nDimensionIndex==0 ) //x-axis
1254 {
1255 if( fCrossesOtherAxis < fMinY )
1256 fCrossesOtherAxis = fMinY;
1257 else if( fCrossesOtherAxis > fMaxY )
1258 fCrossesOtherAxis = fMaxY;
1259
1260 fYStart = fYEnd = fCrossesOtherAxis;
1261 fXEnd=m_pPosHelper->getLogicMaxX();
1262
1263 if(m_nDimension==3)
1264 {
1265 if( AxisHelper::isAxisPositioningEnabled() )
1266 {
1267 if( ::rtl::math::approxEqual( fYOther, fYStart) )
1268 fZStart = fZEnd = fZOnZPlane;
1269 else
1270 fZStart = fZEnd = fZOther;
1271 }
1272 else
1273 {
1274 rStart = getScreenPosition( fXStart, fYStart, fZStart );
1275 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd );
1276
1277 double fDeltaX = rEnd.getX() - rStart.getX();
1278 double fDeltaY = rEnd.getY() - rStart.getY();
1279
1280 //only those points are candidates which are lying on exactly one wall as these are outer edges
1281 tScreenPosAndLogicPosList aPosList { getScreenPosAndLogicPos( fMinX, fYOnYPlane, fZOther ), getScreenPosAndLogicPos( fMinX, fYOther, fZOnZPlane ) };
1282
1283 if( fabs(fDeltaY) > fabs(fDeltaX) )
1284 {
1285 rAlignment.meAlignment = LABEL_ALIGN_LEFT;
1286 //choose most left positions
1287 std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() );
1288 rAlignment.mfLabelDirection = (fDeltaY < 0) ? -1.0 : 1.0;
1289 }
1290 else
1291 {
1292 rAlignment.meAlignment = LABEL_ALIGN_BOTTOM;
1293 //choose most bottom positions
1294 std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() );
1295 rAlignment.mfLabelDirection = (fDeltaX < 0) ? -1.0 : 1.0;
1296 }
1297 ScreenPosAndLogicPos aBestPos( aPosList[0] );
1298 fYStart = fYEnd = aBestPos.fLogicY;
1299 fZStart = fZEnd = aBestPos.fLogicZ;
1300 if( !m_pPosHelper->isMathematicalOrientationX() )
1301 rAlignment.mfLabelDirection *= -1.0;
1302 }
1303 }//end 3D x axis
1304 }
1305 else if( m_nDimensionIndex==1 ) //y-axis
1306 {
1307 if( fCrossesOtherAxis < fMinX )
1308 fCrossesOtherAxis = fMinX;
1309 else if( fCrossesOtherAxis > fMaxX )
1310 fCrossesOtherAxis = fMaxX;
1311
1312 fXStart = fXEnd = fCrossesOtherAxis;
1313 fYEnd=m_pPosHelper->getLogicMaxY();
1314
1315 if(m_nDimension==3)
1316 {
1317 if( AxisHelper::isAxisPositioningEnabled() )
1318 {
1319 if( ::rtl::math::approxEqual( fXOther, fXStart) )
1320 fZStart = fZEnd = fZOnZPlane;
1321 else
1322 fZStart = fZEnd = fZOther;
1323 }
1324 else
1325 {
1326 rStart = getScreenPosition( fXStart, fYStart, fZStart );
1327 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd );
1328
1329 double fDeltaX = rEnd.getX() - rStart.getX();
1330 double fDeltaY = rEnd.getY() - rStart.getY();
1331
1332 //only those points are candidates which are lying on exactly one wall as these are outer edges
1333 tScreenPosAndLogicPosList aPosList { getScreenPosAndLogicPos( fXOnXPlane, fMinY, fZOther ), getScreenPosAndLogicPos( fXOther, fMinY, fZOnZPlane ) };
1334
1335 if( fabs(fDeltaY) > fabs(fDeltaX) )
1336 {
1337 rAlignment.meAlignment = LABEL_ALIGN_LEFT;
1338 //choose most left positions
1339 std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() );
1340 rAlignment.mfLabelDirection = (fDeltaY < 0) ? -1.0 : 1.0;
1341 }
1342 else
1343 {
1344 rAlignment.meAlignment = LABEL_ALIGN_BOTTOM;
1345 //choose most bottom positions
1346 std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() );
1347 rAlignment.mfLabelDirection = (fDeltaX < 0) ? -1.0 : 1.0;
1348 }
1349 ScreenPosAndLogicPos aBestPos( aPosList[0] );
1350 fXStart = fXEnd = aBestPos.fLogicX;
1351 fZStart = fZEnd = aBestPos.fLogicZ;
1352 if( !m_pPosHelper->isMathematicalOrientationY() )
1353 rAlignment.mfLabelDirection *= -1.0;
1354 }
1355 }//end 3D y axis
1356 }
1357 else //z-axis
1358 {
1359 fZEnd = m_pPosHelper->getLogicMaxZ();
1360 if( AxisHelper::isAxisPositioningEnabled() )
1361 {
1362 if( !m_aAxisProperties.m_bSwapXAndY )
1363 {
1364 if( fCrossesOtherAxis < fMinY )
1365 fCrossesOtherAxis = fMinY;
1366 else if( fCrossesOtherAxis > fMaxY )
1367 fCrossesOtherAxis = fMaxY;
1368 fYStart = fYEnd = fCrossesOtherAxis;
1369
1370 if( ::rtl::math::approxEqual( fYOther, fYStart) )
1371 fXStart = fXEnd = fXOnXPlane;
1372 else
1373 fXStart = fXEnd = fXOther;
1374 }
1375 else
1376 {
1377 if( fCrossesOtherAxis < fMinX )
1378 fCrossesOtherAxis = fMinX;
1379 else if( fCrossesOtherAxis > fMaxX )
1380 fCrossesOtherAxis = fMaxX;
1381 fXStart = fXEnd = fCrossesOtherAxis;
1382
1383 if( ::rtl::math::approxEqual( fXOther, fXStart) )
1384 fYStart = fYEnd = fYOnYPlane;
1385 else
1386 fYStart = fYEnd = fYOther;
1387 }
1388 }
1389 else
1390 {
1391 if( !m_pPosHelper->isSwapXAndY() )
1392 {
1393 fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMinX();
1394 fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMinY() : m_pPosHelper->getLogicMaxY();
1395 }
1396 else
1397 {
1398 fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMaxX();
1399 fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMaxY() : m_pPosHelper->getLogicMinY();
1400 }
1401
1402 if(m_nDimension==3)
1403 {
1404 rStart = getScreenPosition( fXStart, fYStart, fZStart );
1405 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd );
1406
1407 double fDeltaX = rEnd.getX() - rStart.getX();
1408
1409 //only those points are candidates which are lying on exactly one wall as these are outer edges
1410 tScreenPosAndLogicPosList aPosList { getScreenPosAndLogicPos( fXOther, fYOnYPlane, fMinZ ), getScreenPosAndLogicPos( fXOnXPlane, fYOther, fMinZ ) };
1411
1412 std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() );
1413 ScreenPosAndLogicPos aBestPos( aPosList[0] );
1414 ScreenPosAndLogicPos aNotSoGoodPos( aPosList[1] );
1415
1416 //choose most bottom positions
1417 if( fDeltaX != 0.0 ) // prefer left-right alignments
1418 {
1419 if( aBestPos.aScreenPos.getX() > aNotSoGoodPos.aScreenPos.getX() )
1420 rAlignment.meAlignment = LABEL_ALIGN_RIGHT;
1421 else
1422 rAlignment.meAlignment = LABEL_ALIGN_LEFT;
1423 }
1424 else
1425 {
1426 if( aBestPos.aScreenPos.getY() > aNotSoGoodPos.aScreenPos.getY() )
1427 rAlignment.meAlignment = LABEL_ALIGN_BOTTOM;
1428 else
1429 rAlignment.meAlignment = LABEL_ALIGN_TOP;
1430 }
1431
1432 rAlignment.mfLabelDirection = (fDeltaX < 0) ? -1.0 : 1.0;
1433 if( !m_pPosHelper->isMathematicalOrientationZ() )
1434 rAlignment.mfLabelDirection *= -1.0;
1435
1436 fXStart = fXEnd = aBestPos.fLogicX;
1437 fYStart = fYEnd = aBestPos.fLogicY;
1438 }
1439 }//end 3D z axis
1440 }
1441
1442 rStart = getScreenPosition( fXStart, fYStart, fZStart );
1443 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd );
1444
1445 if(m_nDimension==3 && !AxisHelper::isAxisPositioningEnabled() )
1446 rAlignment.mfInnerTickDirection = rAlignment.mfLabelDirection;//to behave like before
1447
1448 if(!(m_nDimension==3 && AxisHelper::isAxisPositioningEnabled()) )
1449 return;
1450
1451 double fDeltaX = rEnd.getX() - rStart.getX();
1452 double fDeltaY = rEnd.getY() - rStart.getY();
1453
1454 if( m_nDimensionIndex==2 )
1455 {
1456 if( m_eLeftWallPos != CuboidPlanePosition_Left )
1457 {
1458 rAlignment.mfLabelDirection *= -1.0;
1459 rAlignment.mfInnerTickDirection *= -1.0;
1460 }
1461
1462 rAlignment.meAlignment =
1463 (rAlignment.mfLabelDirection < 0) ?
1465
1466 if( ( fDeltaY<0 && m_aScale.Orientation == chart2::AxisOrientation_REVERSE ) ||
1467 ( fDeltaY>0 && m_aScale.Orientation == chart2::AxisOrientation_MATHEMATICAL ) )
1468 rAlignment.meAlignment =
1469 (rAlignment.meAlignment == LABEL_ALIGN_RIGHT) ?
1471 }
1472 else if( fabs(fDeltaY) > fabs(fDeltaX) )
1473 {
1474 if( m_eBackWallPos != CuboidPlanePosition_Back )
1475 {
1476 rAlignment.mfLabelDirection *= -1.0;
1477 rAlignment.mfInnerTickDirection *= -1.0;
1478 }
1479
1480 rAlignment.meAlignment =
1481 (rAlignment.mfLabelDirection < 0) ?
1483
1484 if( ( fDeltaY<0 && m_aScale.Orientation == chart2::AxisOrientation_REVERSE ) ||
1485 ( fDeltaY>0 && m_aScale.Orientation == chart2::AxisOrientation_MATHEMATICAL ) )
1486 rAlignment.meAlignment =
1487 (rAlignment.meAlignment == LABEL_ALIGN_RIGHT) ?
1489 }
1490 else
1491 {
1492 if( m_eBackWallPos != CuboidPlanePosition_Back )
1493 {
1494 rAlignment.mfLabelDirection *= -1.0;
1495 rAlignment.mfInnerTickDirection *= -1.0;
1496 }
1497
1498 rAlignment.meAlignment =
1499 (rAlignment.mfLabelDirection < 0) ?
1501
1502 if( ( fDeltaX>0 && m_aScale.Orientation == chart2::AxisOrientation_REVERSE ) ||
1503 ( fDeltaX<0 && m_aScale.Orientation == chart2::AxisOrientation_MATHEMATICAL ) )
1504 rAlignment.meAlignment =
1505 (rAlignment.meAlignment == LABEL_ALIGN_TOP) ?
1507 }
1508}
1509
1510TickFactory* VCartesianAxis::createTickFactory()
1511{
1512 return createTickFactory2D();
1513}
1514
1515TickFactory2D* VCartesianAxis::createTickFactory2D()
1516{
1517 AxisLabelAlignment aLabelAlign = m_aAxisProperties.maLabelAlignment;
1518 B2DVector aStart, aEnd;
1519 get2DAxisMainLine(aStart, aEnd, aLabelAlign, getAxisIntersectionValue());
1520
1521 B2DVector aLabelLineStart, aLabelLineEnd;
1522 get2DAxisMainLine(aLabelLineStart, aLabelLineEnd, aLabelAlign, getLabelLineIntersectionValue());
1523 m_aAxisProperties.maLabelAlignment = aLabelAlign;
1524
1525 return new TickFactory2D( m_aScale, m_aIncrement, aStart, aEnd, aLabelLineStart-aStart );
1526}
1527
1529{
1530 TickInfo* pPrevTickInfo = rTickIter.firstInfo();
1531 if (!pPrevTickInfo)
1532 return;
1533
1534 pPrevTickInfo->bPaintIt = true;
1535 for( TickInfo* pTickInfo = rTickIter.nextInfo(); pTickInfo; pTickInfo = rTickIter.nextInfo())
1536 {
1537 pTickInfo->bPaintIt = (pTickInfo->aTickScreenPosition != pPrevTickInfo->aTickScreenPosition);
1538 pPrevTickInfo = pTickInfo;
1539 }
1540}
1541
1542//'hide' tickmarks with identical screen values in aAllTickInfos
1543void VCartesianAxis::hideIdenticalScreenValues( TickInfoArraysType& rTickInfos ) const
1544{
1545 if( isComplexCategoryAxis() || isDateAxis() )
1546 {
1547 sal_Int32 nCount = rTickInfos.size();
1548 for( sal_Int32 nN=0; nN<nCount; nN++ )
1549 {
1550 PureTickIter aTickIter( rTickInfos[nN] );
1551 lcl_hideIdenticalScreenValues( aTickIter );
1552 }
1553 }
1554 else
1555 {
1556 EquidistantTickIter aTickIter( rTickInfos, m_aIncrement, -1 );
1557 lcl_hideIdenticalScreenValues( aTickIter );
1558 }
1559}
1560
1561sal_Int32 VCartesianAxis::estimateMaximumAutoMainIncrementCount()
1562{
1563 sal_Int32 nRet = 10;
1564
1565 if( m_nMaximumTextWidthSoFar==0 && m_nMaximumTextHeightSoFar==0 )
1566 return nRet;
1567
1568 B2DVector aStart, aEnd;
1569 AxisLabelAlignment aLabelAlign = m_aAxisProperties.maLabelAlignment;
1570 get2DAxisMainLine(aStart, aEnd, aLabelAlign, getAxisIntersectionValue());
1571 m_aAxisProperties.maLabelAlignment = aLabelAlign;
1572
1573 sal_Int32 nMaxHeight = static_cast<sal_Int32>(fabs(aEnd.getY()-aStart.getY()));
1574 sal_Int32 nMaxWidth = static_cast<sal_Int32>(fabs(aEnd.getX()-aStart.getX()));
1575
1576 sal_Int32 nTotalAvailable = nMaxHeight;
1577 sal_Int32 nSingleNeeded = m_nMaximumTextHeightSoFar;
1578 sal_Int32 nMaxSameLabel = 0;
1579
1580 // tdf#48041: do not duplicate the value labels because of rounding
1581 if (m_aAxisProperties.m_nAxisType != css::chart2::AxisType::DATE)
1582 {
1583 FixedNumberFormatter aFixedNumberFormatterTest(m_xNumberFormatsSupplier, m_aAxisLabelProperties.m_nNumberFormatKey);
1584 OUString sPreviousValueLabel;
1585 sal_Int32 nSameLabel = 0;
1586 for (auto const & nLabel: m_aAllTickInfos[0])
1587 {
1588 Color nColor = COL_AUTO;
1589 bool bHasColor = false;
1590 OUString sValueLabel = aFixedNumberFormatterTest.getFormattedString(nLabel.fScaledTickValue, nColor, bHasColor);
1591 if (sValueLabel == sPreviousValueLabel)
1592 {
1593 nSameLabel++;
1594 if (nSameLabel > nMaxSameLabel)
1595 nMaxSameLabel = nSameLabel;
1596 }
1597 else
1598 nSameLabel = 0;
1599 sPreviousValueLabel = sValueLabel;
1600 }
1601 }
1602 //for horizontal axis:
1603 if( (m_nDimensionIndex == 0 && !m_aAxisProperties.m_bSwapXAndY)
1604 || (m_nDimensionIndex == 1 && m_aAxisProperties.m_bSwapXAndY) )
1605 {
1606 nTotalAvailable = nMaxWidth;
1607 nSingleNeeded = m_nMaximumTextWidthSoFar;
1608 }
1609
1610 if( nSingleNeeded>0 )
1611 nRet = nTotalAvailable/nSingleNeeded;
1612
1613 if ( nMaxSameLabel > 0 )
1614 {
1615 sal_Int32 nRetNoSameLabel = m_aAllTickInfos[0].size() / (nMaxSameLabel + 1);
1616 if ( nRet > nRetNoSameLabel )
1617 nRet = nRetNoSameLabel;
1618 }
1619
1620 return nRet;
1621}
1622
1623void VCartesianAxis::doStaggeringOfLabels( const AxisLabelProperties& rAxisLabelProperties, TickFactory2D const * pTickFactory2D )
1624{
1625 if( !pTickFactory2D )
1626 return;
1627
1628 if( isComplexCategoryAxis() )
1629 {
1630 sal_Int32 nTextLevelCount = getTextLevelCount();
1631 B2DVector aCumulatedLabelsDistance(0,0);
1632 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ )
1633 {
1634 std::unique_ptr<TickIter> apTickIter(createLabelTickIterator(nTextLevel));
1635 if (apTickIter)
1636 {
1637 double fRotationAngleDegree = m_aAxisLabelProperties.m_fRotationAngleDegree;
1638 if( nTextLevel>0 )
1639 {
1640 lcl_shiftLabels(*apTickIter, aCumulatedLabelsDistance);
1641 //multilevel labels: 0 or 90 by default
1642 if( m_aAxisProperties.m_bSwapXAndY )
1643 fRotationAngleDegree = 90.0;
1644 else
1645 fRotationAngleDegree = 0.0;
1646 }
1647 aCumulatedLabelsDistance += lcl_getLabelsDistance(
1648 *apTickIter, pTickFactory2D->getDistanceAxisTickToText(m_aAxisProperties),
1649 fRotationAngleDegree);
1650 }
1651 }
1652 }
1653 else if (rAxisLabelProperties.isStaggered())
1654 {
1655 if( !m_aAllTickInfos.empty() )
1656 {
1657 LabelIterator aInnerIter( m_aAllTickInfos[0], rAxisLabelProperties.m_eStaggering, true );
1658 LabelIterator aOuterIter( m_aAllTickInfos[0], rAxisLabelProperties.m_eStaggering, false );
1659
1660 lcl_shiftLabels( aOuterIter
1661 , lcl_getLabelsDistance( aInnerIter
1662 , pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties ), 0.0 ) );
1663 }
1664 }
1665}
1666
1667void VCartesianAxis::createDataTableShape(std::unique_ptr<TickFactory2D> const& rpTickFactory2D)
1668{
1669 // Check if we can create the data table shape
1670 // Data table view and m_bDisplayDataTable must be true
1671 if (!m_pDataTableView || !m_aAxisProperties.m_bDisplayDataTable)
1672 return;
1673
1674 m_pDataTableView->initializeShapes(m_xDataTableTarget);
1675 basegfx::B2DVector aStart = rpTickFactory2D->getXaxisStartPos();
1676 basegfx::B2DVector aEnd = rpTickFactory2D->getXaxisEndPos();
1677
1678 rpTickFactory2D->updateScreenValues(m_aAllTickInfos);
1679
1680 sal_Int32 nDistance = -1;
1681
1682 std::unique_ptr<TickIter> apTickIter(createLabelTickIterator(0));
1683 if (apTickIter)
1684 {
1685 nDistance = TickFactory2D::getTickScreenDistance(*apTickIter);
1686 if (getTextLevelCount() > 1)
1687 nDistance *= 2;
1688 }
1689
1690 if (nDistance > 0)
1691 {
1692 m_pDataTableView->createShapes(aStart, aEnd, nDistance);
1693 }
1694}
1695
1696void VCartesianAxis::createLabels()
1697{
1698 if( !prepareShapeCreation() )
1699 return;
1700
1701 std::unique_ptr<TickFactory2D> apTickFactory2D(createTickFactory2D()); // throws on failure
1702
1703 createDataTableShape(apTickFactory2D);
1704
1705 //create labels
1706 if (!m_aAxisProperties.m_bDisplayLabels)
1707 return;
1708
1709 TickFactory2D* pTickFactory2D = apTickFactory2D.get();
1710
1711 //get the transformed screen values for all tickmarks in aAllTickInfos
1712 pTickFactory2D->updateScreenValues( m_aAllTickInfos );
1713 //'hide' tickmarks with identical screen values in aAllTickInfos
1714 hideIdenticalScreenValues( m_aAllTickInfos );
1715
1716 removeTextShapesFromTicks();
1717
1718 //create tick mark text shapes
1719 sal_Int32 nTextLevelCount = getTextLevelCount();
1720 sal_Int32 nScreenDistanceBetweenTicks = -1;
1721 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ )
1722 {
1723 std::unique_ptr< TickIter > apTickIter(createLabelTickIterator( nTextLevel ));
1724 if(apTickIter)
1725 {
1726 if(nTextLevel==0)
1727 {
1728 nScreenDistanceBetweenTicks = TickFactory2D::getTickScreenDistance(*apTickIter);
1729 if( nTextLevelCount>1 )
1730 nScreenDistanceBetweenTicks*=2; //the above used tick iter does contain also the sub ticks -> thus the given distance is only the half
1731 }
1732
1733 AxisLabelProperties aComplexProps(m_aAxisLabelProperties);
1734 if( m_aAxisProperties.m_bComplexCategories )
1735 {
1736 aComplexProps.m_bLineBreakAllowed = true;
1737 aComplexProps.m_bOverlapAllowed = aComplexProps.m_fRotationAngleDegree != 0.0;
1738 if( nTextLevel > 0 )
1739 {
1740 //multilevel labels: 0 or 90 by default
1741 if( m_aAxisProperties.m_bSwapXAndY )
1742 aComplexProps.m_fRotationAngleDegree = 90.0;
1743 else
1744 aComplexProps.m_fRotationAngleDegree = 0.0;
1745 }
1746 }
1747 AxisLabelProperties& rAxisLabelProperties = m_aAxisProperties.m_bComplexCategories ? aComplexProps : m_aAxisLabelProperties;
1748 while (!createTextShapes(m_xTextTarget, *apTickIter, rAxisLabelProperties,
1749 pTickFactory2D, nScreenDistanceBetweenTicks))
1750 {
1751 };
1752 }
1753 }
1754 doStaggeringOfLabels( m_aAxisLabelProperties, pTickFactory2D );
1755
1756 if (m_pDataTableView)
1757 {
1758 sal_Int32 x = m_xTextTarget->getPosition().X;
1759 sal_Int32 y = m_xTextTarget->getPosition().Y;
1760 sal_Int32 height = m_xTextTarget->getSize().Height;
1761 m_pDataTableView->changePosition(x, y + height);
1762 }
1763}
1764
1765void VCartesianAxis::createMaximumLabels()
1766{
1767 m_bRecordMaximumTextSize = true;
1768 const comphelper::ScopeGuard aGuard([this]() { m_bRecordMaximumTextSize = false; });
1769
1770 if( !prepareShapeCreation() )
1771 return;
1772
1773 //create labels
1774 if (!m_aAxisProperties.m_bDisplayLabels)
1775 return;
1776
1777 std::unique_ptr<TickFactory2D> apTickFactory2D(createTickFactory2D()); // throws on failure
1778 TickFactory2D* pTickFactory2D = apTickFactory2D.get();
1779
1780 //get the transformed screen values for all tickmarks in aAllTickInfos
1781 pTickFactory2D->updateScreenValues( m_aAllTickInfos );
1782
1783 //create tick mark text shapes
1784 //@todo: iterate through all tick depth which should be labeled
1785
1786 AxisLabelProperties aAxisLabelProperties( m_aAxisLabelProperties );
1787 if( isAutoStaggeringOfLabelsAllowed( aAxisLabelProperties, pTickFactory2D->isHorizontalAxis(), pTickFactory2D->isVerticalAxis() ) )
1788 aAxisLabelProperties.m_eStaggering = AxisLabelStaggering::StaggerEven;
1789
1790 aAxisLabelProperties.m_bOverlapAllowed = true;
1791 aAxisLabelProperties.m_bLineBreakAllowed = false;
1792 sal_Int32 nTextLevelCount = getTextLevelCount();
1793 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ )
1794 {
1795 std::unique_ptr< TickIter > apTickIter(createMaximumLabelTickIterator( nTextLevel ));
1796 if(apTickIter)
1797 {
1798 while (!createTextShapes(m_xTextTarget, *apTickIter, aAxisLabelProperties,
1799 pTickFactory2D, -1))
1800 {
1801 };
1802 }
1803 }
1804 doStaggeringOfLabels( aAxisLabelProperties, pTickFactory2D );
1805}
1806
1807void VCartesianAxis::updatePositions()
1808{
1809 //update positions of labels
1810 if (!m_aAxisProperties.m_bDisplayLabels)
1811 return;
1812
1813 std::unique_ptr<TickFactory2D> apTickFactory2D(createTickFactory2D()); // throws on failure
1814 TickFactory2D* pTickFactory2D = apTickFactory2D.get();
1815
1816 //update positions of all existing text shapes
1817 pTickFactory2D->updateScreenValues( m_aAllTickInfos );
1818
1819 sal_Int32 nDepth=0;
1820 for (auto const& tickInfos : m_aAllTickInfos)
1821 {
1822 for (auto const& tickInfo : tickInfos)
1823 {
1824 const rtl::Reference<SvxShapeText> & xShape2DText(tickInfo.xTextShape);
1825 if( xShape2DText.is() )
1826 {
1827 B2DVector aTextToTickDistance( pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, true ) );
1828 B2DVector aTickScreenPos2D(tickInfo.aTickScreenPosition);
1829 aTickScreenPos2D += aTextToTickDistance;
1830 awt::Point aAnchorScreenPosition2D(
1831 static_cast<sal_Int32>(aTickScreenPos2D.getX())
1832 ,static_cast<sal_Int32>(aTickScreenPos2D.getY()));
1833
1834 double fRotationAngleDegree = m_aAxisLabelProperties.m_fRotationAngleDegree;
1835 if( nDepth > 0 )
1836 {
1837 //multilevel labels: 0 or 90 by default
1838 if( pTickFactory2D->isHorizontalAxis() )
1839 fRotationAngleDegree = 0.0;
1840 else
1841 fRotationAngleDegree = 90;
1842 }
1843
1844 // #i78696# use mathematically correct rotation now
1845 const double fRotationAnglePi(-basegfx::deg2rad(fRotationAngleDegree));
1846 uno::Any aATransformation = ShapeFactory::makeTransformation(aAnchorScreenPosition2D, fRotationAnglePi);
1847
1848 //set new position
1849 try
1850 {
1851 xShape2DText->SvxShape::setPropertyValue( "Transformation", aATransformation );
1852 }
1853 catch( const uno::Exception& )
1854 {
1855 TOOLS_WARN_EXCEPTION("chart2", "" );
1856 }
1857
1858 //correctPositionForRotation
1859 LabelPositionHelper::correctPositionForRotation( xShape2DText
1860 , m_aAxisProperties.maLabelAlignment.meAlignment, fRotationAngleDegree, m_aAxisProperties.m_bComplexCategories );
1861 }
1862 }
1863 ++nDepth;
1864 }
1865
1866 doStaggeringOfLabels( m_aAxisLabelProperties, pTickFactory2D );
1867}
1868
1869void VCartesianAxis::createTickMarkLineShapes( TickInfoArrayType& rTickInfos, const TickmarkProperties& rTickmarkProperties, TickFactory2D const & rTickFactory2D, bool bOnlyAtLabels )
1870{
1871 sal_Int32 nPointCount = rTickInfos.size();
1872 drawing::PointSequenceSequence aPoints(2*nPointCount);
1873
1874 sal_Int32 nN = 0;
1875 for (auto const& tickInfo : rTickInfos)
1876 {
1877 if( !tickInfo.bPaintIt )
1878 continue;
1879
1880 bool bTicksAtLabels = ( m_aAxisProperties.m_eTickmarkPos != css::chart::ChartAxisMarkPosition_AT_AXIS );
1881 double fInnerDirectionSign = m_aAxisProperties.maLabelAlignment.mfInnerTickDirection;
1882 if( bTicksAtLabels && m_aAxisProperties.m_eLabelPos == css::chart::ChartAxisLabelPosition_OUTSIDE_END )
1883 fInnerDirectionSign *= -1.0;
1884 bTicksAtLabels = bTicksAtLabels || bOnlyAtLabels;
1885 //add ticks at labels:
1886 rTickFactory2D.addPointSequenceForTickLine( aPoints, nN++, tickInfo.fScaledTickValue
1887 , fInnerDirectionSign , rTickmarkProperties, bTicksAtLabels );
1888 //add ticks at axis (without labels):
1889 if( !bOnlyAtLabels && m_aAxisProperties.m_eTickmarkPos == css::chart::ChartAxisMarkPosition_AT_LABELS_AND_AXIS )
1890 rTickFactory2D.addPointSequenceForTickLine( aPoints, nN++, tickInfo.fScaledTickValue
1891 , m_aAxisProperties.maLabelAlignment.mfInnerTickDirection, rTickmarkProperties, !bTicksAtLabels );
1892 }
1893 aPoints.realloc(nN);
1894 ShapeFactory::createLine2D( m_xGroupShape_Shapes, aPoints
1895 , &rTickmarkProperties.aLineProperties );
1896}
1897
1898void VCartesianAxis::createShapes()
1899{
1900 if( !prepareShapeCreation() )
1901 return;
1902
1903 //create line shapes
1904 if(m_nDimension==2)
1905 {
1906 std::unique_ptr<TickFactory2D> apTickFactory2D(createTickFactory2D()); // throws on failure
1907 TickFactory2D* pTickFactory2D = apTickFactory2D.get();
1908
1909 //create extra long ticks to separate complex categories (create them only there where the labels are)
1910 if( isComplexCategoryAxis() )
1911 {
1912 TickInfoArraysType aComplexTickInfos;
1913 createAllTickInfosFromComplexCategories( aComplexTickInfos, true );
1914 pTickFactory2D->updateScreenValues( aComplexTickInfos );
1915 hideIdenticalScreenValues( aComplexTickInfos );
1916
1917 std::vector<TickmarkProperties> aTickmarkPropertiesList;
1918 static const bool bIncludeSpaceBetweenTickAndText = false;
1919 sal_Int32 nOffset = static_cast<sal_Int32>(pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, false, bIncludeSpaceBetweenTickAndText ).getLength());
1920 sal_Int32 nTextLevelCount = getTextLevelCount();
1921 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ )
1922 {
1923 std::unique_ptr< TickIter > apTickIter(createLabelTickIterator( nTextLevel ));
1924 if( apTickIter )
1925 {
1926 double fRotationAngleDegree = m_aAxisLabelProperties.m_fRotationAngleDegree;
1927 if( nTextLevel > 0 )
1928 {
1929 //Multi-level Labels: default to 0 or 90
1930 if( m_aAxisProperties.m_bSwapXAndY )
1931 fRotationAngleDegree = 90.0;
1932 else
1933 fRotationAngleDegree = 0.0;
1934 }
1935 B2DVector aLabelsDistance(lcl_getLabelsDistance(
1936 *apTickIter, pTickFactory2D->getDistanceAxisTickToText(m_aAxisProperties),
1937 fRotationAngleDegree));
1938 sal_Int32 nCurrentLength = static_cast<sal_Int32>(aLabelsDistance.getLength());
1939 aTickmarkPropertiesList.push_back( m_aAxisProperties.makeTickmarkPropertiesForComplexCategories( nOffset + nCurrentLength, 0 ) );
1940 nOffset += nCurrentLength;
1941 }
1942 }
1943
1944 sal_Int32 nTickmarkPropertiesCount = aTickmarkPropertiesList.size();
1945 TickInfoArraysType::iterator aDepthIter = aComplexTickInfos.begin();
1946 const TickInfoArraysType::const_iterator aDepthEnd = aComplexTickInfos.end();
1947 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd && nDepth < nTickmarkPropertiesCount; ++aDepthIter, nDepth++ )
1948 {
1949 if(nDepth==0 && !m_aAxisProperties.m_nMajorTickmarks)
1950 continue;
1951 createTickMarkLineShapes( *aDepthIter, aTickmarkPropertiesList[nDepth], *pTickFactory2D, true /*bOnlyAtLabels*/ );
1952 }
1953 }
1954 //create normal ticks for major and minor intervals
1955 {
1956 TickInfoArraysType aUnshiftedTickInfos;
1957 if( m_aScale.m_bShiftedCategoryPosition )// if m_bShiftedCategoryPosition==true the tickmarks in m_aAllTickInfos are shifted
1958 {
1959 pTickFactory2D->getAllTicks( aUnshiftedTickInfos );
1960 pTickFactory2D->updateScreenValues( aUnshiftedTickInfos );
1961 hideIdenticalScreenValues( aUnshiftedTickInfos );
1962 }
1963 TickInfoArraysType& rAllTickInfos = m_aScale.m_bShiftedCategoryPosition ? aUnshiftedTickInfos : m_aAllTickInfos;
1964
1965 if (rAllTickInfos.empty())
1966 return;
1967
1968 sal_Int32 nDepth = 0;
1969 sal_Int32 nTickmarkPropertiesCount = m_aAxisProperties.m_aTickmarkPropertiesList.size();
1970 for( auto& rTickInfos : rAllTickInfos )
1971 {
1972 if (nDepth == nTickmarkPropertiesCount)
1973 break;
1974
1975 createTickMarkLineShapes( rTickInfos, m_aAxisProperties.m_aTickmarkPropertiesList[nDepth], *pTickFactory2D, false /*bOnlyAtLabels*/ );
1976 nDepth++;
1977 }
1978 }
1979 //create axis main lines
1980 //it serves also as the handle shape for the axis selection
1981 {
1982 drawing::PointSequenceSequence aPoints(1);
1983 apTickFactory2D->createPointSequenceForAxisMainLine( aPoints );
1984 rtl::Reference<SvxShapePolyPolygon> xShape = ShapeFactory::createLine2D(
1985 m_xGroupShape_Shapes, aPoints
1986 , &m_aAxisProperties.m_aLineProperties );
1987 //because of this name this line will be used for marking the axis
1988 ::chart::ShapeFactory::setShapeName( xShape, "MarkHandles" );
1989 }
1990 //create an additional line at NULL
1991 if( !AxisHelper::isAxisPositioningEnabled() )
1992 {
1993 double fExtraLineCrossesOtherAxis = getExtraLineIntersectionValue();
1994 if (!std::isnan(fExtraLineCrossesOtherAxis))
1995 {
1996 B2DVector aStart, aEnd;
1997 AxisLabelAlignment aLabelAlign = m_aAxisProperties.maLabelAlignment;
1998 get2DAxisMainLine(aStart, aEnd, aLabelAlign, fExtraLineCrossesOtherAxis);
1999 m_aAxisProperties.maLabelAlignment = aLabelAlign;
2000 drawing::PointSequenceSequence aPoints{{
2001 {static_cast<sal_Int32>(aStart.getX()), static_cast<sal_Int32>(aStart.getY())},
2002 {static_cast<sal_Int32>(aEnd.getX()), static_cast<sal_Int32>(aEnd.getY())} }};
2003 ShapeFactory::createLine2D(
2004 m_xGroupShape_Shapes, aPoints, &m_aAxisProperties.m_aLineProperties );
2005 }
2006 }
2007 }
2008
2009 createLabels();
2010}
2011
2012void VCartesianAxis::createDataTableView(std::vector<std::unique_ptr<VSeriesPlotter>>& rSeriesPlotterList,
2013 Reference<util::XNumberFormatsSupplier> const& xNumberFormatsSupplier,
2014 rtl::Reference<::chart::ChartModel> const& xChartDoc,
2015 css::uno::Reference<css::uno::XComponentContext> const& rComponentContext)
2016{
2017 if (!m_aAxisProperties.m_bDisplayDataTable)
2018 return;
2019
2020 m_pDataTableView.reset(new DataTableView(xChartDoc, m_aAxisProperties.m_xDataTableModel, rComponentContext, m_aAxisProperties.m_bDataTableAlignAxisValuesWithColumns));
2021 m_pDataTableView->initializeValues(rSeriesPlotterList);
2022 m_xNumberFormatsSupplier = xNumberFormatsSupplier;
2023}
2024
2025
2026} //namespace chart
2027
2028/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
PureTickIter m_aPureTickIter
size_t m_nCurrentIndex
const AxisLabelStaggering m_eAxisLabelStaggering
std::vector< size_t > m_aValidIndices
TickInfoArrayType & m_rTickInfoVector
bool m_bInnerLine
virtual void SAL_CALL setString(const OUString &aString) override
virtual css::awt::Point SAL_CALL getPosition() override
virtual css::awt::Size SAL_CALL getSize() override
virtual SvxTextForwarder * GetTextForwarder() override
virtual sal_Int32 GetLineCount(sal_Int32 nPara) const=0
virtual void GetLineBoundaries(sal_Int32 &rStart, sal_Int32 &rEnd, sal_Int32 nParagraph, sal_Int32 nLine) const=0
virtual sal_Int32 GetParagraphCount() const=0
virtual bool GetWordIndices(sal_Int32 nPara, sal_Int32 nIndex, sal_Int32 &rStart, sal_Int32 &rEnd) const=0
void rotate(double fRadiant)
void translate(double fX, double fY)
double getLength() const
bool isInside(const Tuple2D< TYPE > &rTuple) const
TYPE getX() const
TYPE getY() const
DataTableView is responsible to create the table object, set the cell properties accordingly to the m...
OUString getFormattedString(double fValue, Color &rLabelColor, bool &rbColorChanged) const
static void correctPositionForRotation(const rtl::Reference< SvxShapeText > &xShape2DText, LabelAlignment eLabelAlignment, const double fRotationAngle, bool bRotateAroundCenter)
PlottingPositionHelper * m_pPosHelper
Definition: PlotterBase.hxx:75
static void setMultiProperties(const tNameSequence &rNames, const tAnySequence &rValues, SvxShape &xTarget)
static void setShapeName(const rtl::Reference< SvxShape > &xShape, const OUString &rName)
static OUString getStackedString(const OUString &rString, bool bStacked)
static css::awt::Size getSizeAfterRotation(SvxShape &rShape, double fRotationAngleDegree)
static rtl::Reference< SvxShapeText > createText(const rtl::Reference< SvxShapeGroupAnyD > &xTarget2D, const OUString &rText, const tNameSequence &rPropNames, const tAnySequence &rPropValues, const css::uno::Any &rATransformation)
static css::uno::Any makeTransformation(const css::awt::Point &rScreenPosition2D, double fRotationAnglePi=0.0)
bool isHorizontalAxis() const
Definition: Tickmarks.cxx:169
const ::basegfx::B2DVector & getXaxisEndPos() const
Definition: Tickmarks.hxx:141
void addPointSequenceForTickLine(css::drawing::PointSequenceSequence &rPoints, sal_Int32 nSequenceIndex, double fScaledLogicTickValue, double fInnerDirectionSign, const TickmarkProperties &rTickmarkProperties, bool bPlaceAtLabels) const
Definition: Tickmarks.cxx:215
::basegfx::B2DVector getDistanceAxisTickToText(const AxisProperties &rAxisProperties, bool bIncludeFarAwayDistanceIfSo=false, bool bIncludeSpaceBetweenTickAndText=true) const
Definition: Tickmarks.cxx:242
bool isVerticalAxis() const
Definition: Tickmarks.cxx:181
void updateScreenValues(TickInfoArraysType &rAllTickInfos) const
Determine the screen positions of all ticks based on their numeric values.
Definition: Tickmarks.cxx:303
const ::basegfx::B2DVector & getXaxisStartPos() const
Definition: Tickmarks.hxx:136
void getAllTicks(TickInfoArraysType &rAllTickInfos) const
Definition: Tickmarks.cxx:119
virtual TickInfo * nextInfo()=0
virtual TickInfo * firstInfo()=0
VCartesianAxis(const AxisProperties &rAxisProperties, const css::uno::Reference< css::util::XNumberFormatsSupplier > &xNumberFormatsSupplier, sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount, PlottingPositionHelper *pPosHelper=nullptr)
virtual ~VCartesianAxis() override
constexpr ::Color COL_AUTO(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
int nCount
#define TOOLS_WARN_EXCEPTION(area, stream)
float u
float y
float x
Reference< XInterface > xTarget
sal_Int32 nIndex
double getLength(const B2DPolygon &rCandidate)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
constexpr double deg2rad(double v)
size
OOO_DLLPUBLIC_CHARTTOOLS::basegfx::B2IRectangle makeRectangle(const css::awt::Point &rPosition, const css::awt::Size &rSize)
static OUString getTextLabelString(const FixedNumberFormatter &rFixedNumberFormatter, const uno::Sequence< OUString > *pCategories, const TickInfo *pTickInfo, bool bComplexCat, Color &rExtraColor, bool &rHasExtraColor)
static void getAxisLabelProperties(tNameSequence &rPropNames, tAnySequence &rPropValues, const AxisProperties &rAxisProp, const AxisLabelProperties &rAxisLabelProp, sal_Int32 nLimitedSpaceForText, bool bLimitedHeight)
static bool lcl_doesShapeOverlapWithTickmark(SvxShape &rShape, double fRotationAngleDegree, const basegfx::B2DVector &rTickScreenPosition)
static void lcl_hideIdenticalScreenValues(TickIter &rTickIter)
static bool lcl_hasWordBreak(const rtl::Reference< SvxShapeText > &xShape)
static void lcl_shiftLabels(TickIter &rIter, const B2DVector &rStaggerDistance)
css::uno::Sequence< OUString > tNameSequence
static B2DVector lcl_getLabelsDistance(TickIter &rIter, const B2DVector &rDistanceTickToText, double fRotationAngleDegree)
std::vector< TickInfoArrayType > TickInfoArraysType
Definition: Tickmarks.hxx:59
std::unordered_map< OUString, OUString > tPropertyNameMap
@ LABEL_ALIGN_TOP
@ LABEL_ALIGN_RIGHT
@ LABEL_ALIGN_LEFT
@ LABEL_ALIGN_BOTTOM
static bool doesOverlap(const rtl::Reference< SvxShapeText > &xShape1, const rtl::Reference< SvxShapeText > &xShape2, double fRotationAngleDegree)
css::uno::Sequence< css::uno::Any > tAnySequence
@ CuboidPlanePosition_Left
@ CuboidPlanePosition_Back
@ CuboidPlanePosition_Bottom
std::vector< TickInfo > TickInfoArrayType
Definition: Tickmarks.hxx:58
static rtl::Reference< SvxShapeText > createSingleLabel(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const awt::Point &rAnchorScreenPosition2D, const OUString &rLabel, const AxisLabelProperties &rAxisLabelProperties, const AxisProperties &rAxisProperties, const tNameSequence &rPropNames, const tAnySequence &rPropValues, const bool bIsHorizontalAxis)
static void lcl_ResizeTextShapeToFitAvailableSpace(SvxShapeText &rShape2DText, const AxisLabelProperties &rAxisLabelProperties, std::u16string_view rLabel, const tNameSequence &rPropNames, const tAnySequence &rPropValues, const bool bIsHorizontalAxis)
std::vector< VCartesianAxis::ScreenPosAndLogicPos > tScreenPosAndLogicPosList
static void removeShapesAtWrongRhythm(TickIter &rIter, sal_Int32 nCorrectRhythm, sal_Int32 nMaxTickToCheck, const rtl::Reference< SvxShapeGroupAnyD > &xTarget)
static void lcl_getRotatedPolygon(B2DPolygon &aPoly, const ::basegfx::B2DRectangle &aRect, const awt::Point &aPos, const double fRotationAngleDegree)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
const PropertyStruct aPropNames[]
double mfInnerTickDirection
which direction the labels are to be drawn.
LabelAlignment meAlignment
which direction the inner tickmarks are to be drawn.
css::awt::Size m_aFontReferenceSize
AxisLabelStaggering m_eStaggering
css::awt::Rectangle m_aMaximumSpaceForLabels
AxisLabelAlignment maLabelAlignment
rtl::Reference<::chart::Axis > m_xAxisModel
double getUnscaledTickValue() const
Return a value associated with the tick mark.
Definition: Tickmarks.cxx:43
::basegfx::B2DVector aTickScreenPosition
Definition: Tickmarks.hxx:41
sal_Int32 nFactorForLimitedTextWidth
Definition: Tickmarks.hxx:42
double fScaledTickValue
Definition: Tickmarks.hxx:37
OUString aText
Definition: Tickmarks.hxx:40
rtl::Reference< SvxShapeText > xTextShape
Definition: Tickmarks.hxx:39
OUString aLabel