LibreOffice Module chart2 (master) 1
PieChart.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 <BaseGFXHelper.hxx>
21#include <VLineProperties.hxx>
22#include "PieChart.hxx"
24#include <ShapeFactory.hxx>
26#include <CommonConverters.hxx>
27#include <ObjectIdentifier.hxx>
28#include <ChartType.hxx>
29#include <DataSeries.hxx>
31
32#include <com/sun/star/chart/DataLabelPlacement.hpp>
33#include <com/sun/star/chart2/XColorScheme.hpp>
34
35#include <com/sun/star/drawing/XShapes.hpp>
36#include <sal/log.hxx>
37#include <osl/diagnose.h>
39#include <tools/helpers.hxx>
40
41#include <limits>
42#include <memory>
43
44using namespace ::com::sun::star;
45using namespace ::com::sun::star::chart2;
46using namespace ::chart::DataSeriesProperties;
47
48namespace chart {
49
51{
55
59
63
67
81
85
88 double mfLogicZ;
89
92 double mfDepth;
93
100 mfLogicYSum(0.0),
101 mfLogicZ(0.0),
102 mfDepth(0.0) {}
103};
104
105namespace
106{
107::basegfx::B2IRectangle lcl_getRect(const rtl::Reference<SvxShape>& xShape)
108{
110 if (xShape.is())
111 aRect = BaseGFXHelper::makeRectangle(xShape->getPosition(), xShape->getSize());
112 return aRect;
113}
114
115bool lcl_isInsidePage(const awt::Point& rPos, const awt::Size& rSize, const awt::Size& rPageSize)
116{
117 if (rPos.X < 0 || rPos.Y < 0)
118 return false;
119 if ((rPos.X + rSize.Width) > rPageSize.Width)
120 return false;
121 if ((rPos.Y + rSize.Height) > rPageSize.Height)
122 return false;
123 return true;
124}
125
126} //end anonymous namespace
127
129{
130public:
131 PiePositionHelper( double fAngleDegreeOffset );
132
133 bool getInnerAndOuterRadius( double fCategoryX, double& fLogicInnerRadius, double& fLogicOuterRadius, bool bUseRings, double fMaxOffset ) const;
134
135public:
136 //Distance between different category rings, seen relative to width of a ring:
137 double m_fRingDistance; //>=0 m_fRingDistance=1 --> distance == width
138};
139
140PiePositionHelper::PiePositionHelper( double fAngleDegreeOffset )
141 : m_fRingDistance(0.0)
142{
143 m_fRadiusOffset = 0.0;
144 m_fAngleDegreeOffset = fAngleDegreeOffset;
145}
146
160 , double& fLogicInnerRadius, double& fLogicOuterRadius
161 , bool bUseRings, double fMaxOffset ) const
162{
163 if( !bUseRings )
164 fCategoryX = 1.0;
165
166 double fLogicInner = fCategoryX -0.5+m_fRingDistance/2.0;
167 double fLogicOuter = fCategoryX +0.5-m_fRingDistance/2.0;
168
170 {
171 //in this case the given getMaximumX() was not correct instead the minimum should have been smaller by fMaxOffset
172 //but during getMaximumX and getMimumX we do not know the axis orientation
173 fLogicInner += fMaxOffset;
174 fLogicOuter += fMaxOffset;
175 }
176
177 if( fLogicInner >= getLogicMaxX() )
178 return false;
179 if( fLogicOuter <= getLogicMinX() )
180 return false;
181
182 if( fLogicInner < getLogicMinX() )
183 fLogicInner = getLogicMinX();
184 if( fLogicOuter > getLogicMaxX() )
185 fLogicOuter = getLogicMaxX();
186
187 fLogicInnerRadius = fLogicInner;
188 fLogicOuterRadius = fLogicOuter;
190 std::swap(fLogicInnerRadius,fLogicOuterRadius);
191 return true;
192}
193
194PieChart::PieChart( const rtl::Reference<ChartType>& xChartTypeModel
195 , sal_Int32 nDimensionCount
196 , bool bExcludingPositioning )
197 : VSeriesPlotter( xChartTypeModel, nDimensionCount )
198 , m_pPosHelper( new PiePositionHelper( (m_nDimension==3) ? 0.0 : 90.0 ) )
199 , m_bUseRings(false)
200 , m_bSizeExcludesLabelsAndExplodedSegments(bExcludingPositioning)
201 , m_fMaxOffset(std::numeric_limits<double>::quiet_NaN())
202{
203 PlotterBase::m_pPosHelper = m_pPosHelper.get();
204 VSeriesPlotter::m_pMainPosHelper = m_pPosHelper.get();
205 m_pPosHelper->m_fRadiusOffset = 0.0;
206 m_pPosHelper->m_fRingDistance = 0.0;
207
208 if( !xChartTypeModel.is() )
209 return;
210
211 try
212 {
213 xChartTypeModel->getFastPropertyValue(PROP_PIECHARTTYPE_USE_RINGS) >>= m_bUseRings; // "UseRings"
214 if( m_bUseRings )
215 {
216 m_pPosHelper->m_fRadiusOffset = 1.0;
217 if( nDimensionCount==3 )
218 m_pPosHelper->m_fRingDistance = 0.1;
219 }
220 }
221 catch( const uno::Exception& )
222 {
223 TOOLS_WARN_EXCEPTION("chart2", "" );
224 }
225}
226
228{
229}
230
231void PieChart::setScales( std::vector< ExplicitScaleData >&& rScales, bool /* bSwapXAndYAxis */ )
232{
233 OSL_ENSURE(m_nDimension<=static_cast<sal_Int32>(rScales.size()),"Dimension of Plotter does not fit two dimension of given scale sequence");
234 m_pPosHelper->setScales( std::move(rScales), true );
235}
236
238{
239 if( m_nDimension == 3 )
240 return drawing::Direction3D(1,1,0.10);
241 return drawing::Direction3D(1,1,1);
242}
243
245{
246 return true;
247}
248
251 const uno::Reference<beans::XPropertySet>& xObjectProperties,
252 const ShapeParam& rParam,
253 const sal_Int32 nPointCount,
254 const bool bConcentricExplosion)
255{
256 //transform position:
257 drawing::Direction3D aOffset;
258 double fExplodedInnerRadius = rParam.mfUnitCircleInnerRadius;
259 double fExplodedOuterRadius = rParam.mfUnitCircleOuterRadius;
260 double fStartAngle = rParam.mfUnitCircleStartAngleDegree;
261 double fWidthAngle = rParam.mfUnitCircleWidthAngleDegree;
262
263 if (rParam.mfExplodePercentage != 0.0) {
264 double fRadius = (fExplodedOuterRadius-fExplodedInnerRadius)*rParam.mfExplodePercentage;
265
266 if (bConcentricExplosion) {
267
268 // For concentric explosion, increase the radius but retain the original
269 // arc length of all ring segments together. This results in a gap
270 // that's evenly divided among all segments, assuming they all have
271 // the same explosion percentage
272 assert(fExplodedInnerRadius >= 0 && fExplodedOuterRadius > 0);
273 double fAngleRatio = (fExplodedInnerRadius + fExplodedOuterRadius) /
274 (fExplodedInnerRadius + fExplodedOuterRadius + 2 * fRadius);
275
276 assert(nPointCount > 0);
277 double fAngleGap = 360 * (1.0 - fAngleRatio) / nPointCount;
278 fStartAngle += fAngleGap / 2;
279 fWidthAngle -= fAngleGap;
280
281 fExplodedInnerRadius += fRadius;
282 fExplodedOuterRadius += fRadius;
283
284 } else {
285 // For the non-concentric explosion case, keep the original radius
286 // but shift the circle origin
287 double fAngle = fStartAngle + fWidthAngle/2.0;
288
289 drawing::Position3D aOrigin = m_pPosHelper->transformUnitCircleToScene(0, 0, rParam.mfLogicZ);
290 drawing::Position3D aNewOrigin = m_pPosHelper->transformUnitCircleToScene(fAngle, fRadius, rParam.mfLogicZ);
291 aOffset = aNewOrigin - aOrigin;
292 }
293 }
294
295 //create point
297 if(m_nDimension==3)
298 {
300 , fStartAngle, fWidthAngle
301 , fExplodedInnerRadius, fExplodedOuterRadius
302 , aOffset, B3DHomMatrixToHomogenMatrix( m_pPosHelper->getUnitCartesianToScene() )
303 , rParam.mfDepth );
304 }
305 else
306 {
308 , fStartAngle, fWidthAngle
309 , fExplodedInnerRadius, fExplodedOuterRadius
310 , aOffset, B3DHomMatrixToHomogenMatrix( m_pPosHelper->getUnitCartesianToScene() ) );
311 }
313 return xShape;
314}
315
317 const rtl::Reference<SvxShapeGroupAnyD>& xTextTarget,
318 VDataSeries& rSeries, sal_Int32 nPointIndex, ShapeParam& rParam )
319{
320 if (!rSeries.getDataPointLabelIfLabel(nPointIndex))
321 // There is no text label for this data point. Nothing to do.
322 return;
323
328 if (rParam.mfExplodePercentage != 0.0)
329 {
330 double fExplodeOffset = (rParam.mfUnitCircleOuterRadius-rParam.mfUnitCircleInnerRadius)*rParam.mfExplodePercentage;
331 rParam.mfUnitCircleInnerRadius += fExplodeOffset;
332 rParam.mfUnitCircleOuterRadius += fExplodeOffset;
333 }
334
337 sal_Int32 nLabelPlacement = rSeries.getLabelPlacement(
338 nPointIndex, m_xChartTypeModel, m_pPosHelper->isSwapXAndY());
339
343
344 double nVal = rSeries.getYValue(nPointIndex);
345 //AVOID_OVERLAP is in fact "Best fit" in the UI.
346 bool bMovementAllowed = nLabelPlacement == css::chart::DataLabelPlacement::AVOID_OVERLAP
347 || nLabelPlacement == css::chart::DataLabelPlacement::CUSTOM;
348 if( bMovementAllowed )
349 nLabelPlacement = css::chart::DataLabelPlacement::CENTER;
350
360 sal_Int32 nScreenValueOffsetInRadiusDirection = 0 ;
361 if( nLabelPlacement == css::chart::DataLabelPlacement::OUTSIDE )
362 nScreenValueOffsetInRadiusDirection = (m_nDimension!=3) ? 150 : 0;//todo maybe calculate this font height dependent
363 else if( nLabelPlacement == css::chart::DataLabelPlacement::INSIDE )
364 nScreenValueOffsetInRadiusDirection = (m_nDimension!=3) ? -150 : 0;//todo maybe calculate this font height dependent
365
370 awt::Point aScreenPosition2D(
371 aPolarPosHelper.getLabelScreenPositionAndAlignmentForUnitCircleValues(eAlignment, nLabelPlacement
373 , rParam.mfUnitCircleInnerRadius, rParam.mfUnitCircleOuterRadius, rParam.mfLogicZ+0.5, 0 ));
374
376 PieLabelInfo aPieLabelInfo;
377 aPieLabelInfo.aFirstPosition = basegfx::B2IVector( aScreenPosition2D.X, aScreenPosition2D.Y );
378 awt::Point aOrigin( aPolarPosHelper.transformSceneToScreenPosition( m_pPosHelper->transformUnitCircleToScene( 0.0, 0.0, rParam.mfLogicZ+1.0 ) ) );
379 aPieLabelInfo.aOrigin = basegfx::B2IVector( aOrigin.X, aOrigin.Y );
380
382 if( nScreenValueOffsetInRadiusDirection != 0)
383 {
384 basegfx::B2IVector aDirection( aScreenPosition2D.X- aOrigin.X, aScreenPosition2D.Y- aOrigin.Y );
385 aDirection.setLength(nScreenValueOffsetInRadiusDirection);
386 aScreenPosition2D.X += aDirection.getX();
387 aScreenPosition2D.Y += aDirection.getY();
388 }
389
390 // compute outer pie radius
391 awt::Point aOuterCirclePoint = PlottingPositionHelper::transformSceneToScreenPosition(
392 m_pPosHelper->transformUnitCircleToScene(
393 0,
395 0 ),
397 basegfx::B2IVector aRadiusVector(
398 aOuterCirclePoint.X - aPieLabelInfo.aOrigin.getX(),
399 aOuterCirclePoint.Y - aPieLabelInfo.aOrigin.getY() );
400 double fSquaredPieRadius = aRadiusVector.scalar(aRadiusVector);
401 double fPieRadius = sqrt( fSquaredPieRadius );
402 double fAngleDegree
404 while (fAngleDegree > 360.0)
405 fAngleDegree -= 360.0;
406 while (fAngleDegree < 0.0)
407 fAngleDegree += 360.0;
408
410 m_pPosHelper->transformUnitCircleToScene(fAngleDegree, rParam.mfUnitCircleOuterRadius, 0),
412 aPieLabelInfo.aOuterPosition = basegfx::B2IVector(aOuterPosition.X, aOuterPosition.Y);
413
414 // set the maximum text width to be used when text wrapping is enabled
415 double fTextMaximumFrameWidth = 0.8 * fPieRadius;
416 if (nLabelPlacement == css::chart::DataLabelPlacement::OUTSIDE
418 {
419 if ((fAngleDegree >= 67.5 && fAngleDegree <= 112.5)
420 || (fAngleDegree >= 247.5 && fAngleDegree <= 292.5))
421 fTextMaximumFrameWidth = m_aAvailableOuterRect.getWidth() / 3.0;
422 else
423 fTextMaximumFrameWidth = 0.85 * (m_aAvailableOuterRect.getWidth() / 2.0 - fPieRadius);
424 }
425 sal_Int32 nTextMaximumFrameWidth = ceil(fTextMaximumFrameWidth);
426
428 aPieLabelInfo.xTextShape = createDataLabel(
429 xTextTarget, rSeries, nPointIndex, nVal, rParam.mfLogicYSum,
430 aScreenPosition2D, eAlignment, 0, nTextMaximumFrameWidth);
431
434 rtl::Reference< SvxShape > xChild = aPieLabelInfo.xTextShape;
435
437 if( !xChild.is() )
438 return;
439
440 aPieLabelInfo.xLabelGroupShape = dynamic_cast<SvxShapeGroupAnyD*>(xChild->getParent().get());
441
442 if (bMovementAllowed && !m_bUseRings)
443 {
448 if (rSeries.getLabelPlacement(nPointIndex, m_xChartTypeModel, m_pPosHelper->isSwapXAndY())
449 == css::chart::DataLabelPlacement::CUSTOM
450 || !performLabelBestFitInnerPlacement(rParam, aPieLabelInfo))
451 {
453 {
454 if ((fAngleDegree >= 67.5 && fAngleDegree <= 112.5)
455 || (fAngleDegree >= 247.5 && fAngleDegree <= 292.5))
456 fTextMaximumFrameWidth = m_aAvailableOuterRect.getWidth() / 3.0;
457 else
458 fTextMaximumFrameWidth
459 = 0.85 * (m_aAvailableOuterRect.getWidth() / 2.0 - fPieRadius);
460 nTextMaximumFrameWidth = ceil(fTextMaximumFrameWidth);
461 }
462
463 nScreenValueOffsetInRadiusDirection = (m_nDimension != 3) ? 150 : 0;
464 aScreenPosition2D
466 eAlignment, css::chart::DataLabelPlacement::OUTSIDE,
469 rParam.mfUnitCircleOuterRadius, rParam.mfLogicZ + 0.5, 0);
470 aPieLabelInfo.aFirstPosition
471 = basegfx::B2IVector(aScreenPosition2D.X, aScreenPosition2D.Y);
472
473 //add a scaling independent Offset if requested
474 if (nScreenValueOffsetInRadiusDirection != 0)
475 {
476 basegfx::B2IVector aDirection(aScreenPosition2D.X - aOrigin.X,
477 aScreenPosition2D.Y - aOrigin.Y);
478 aDirection.setLength(nScreenValueOffsetInRadiusDirection);
479 aScreenPosition2D.X += aDirection.getX();
480 aScreenPosition2D.Y += aDirection.getY();
481 }
482
483 uno::Reference<drawing::XShapes> xShapes(xChild->getParent(), uno::UNO_QUERY);
484 xShapes->remove(aPieLabelInfo.xTextShape);
485 aPieLabelInfo.xTextShape
486 = createDataLabel(xTextTarget, rSeries, nPointIndex, nVal, rParam.mfLogicYSum,
487 aScreenPosition2D, eAlignment, 0, nTextMaximumFrameWidth);
488 xChild = aPieLabelInfo.xTextShape;
489 if (!xChild.is())
490 return;
491
492 aPieLabelInfo.xLabelGroupShape = dynamic_cast<SvxShapeGroupAnyD*>(xChild->getParent().get());
493 }
494 }
495
496 bool bShowLeaderLine = rSeries.getModel()
497 ->getFastPropertyValue(PROP_DATASERIES_SHOW_CUSTOM_LEADERLINES) // "ShowCustomLeaderLines"
498 .get<sal_Bool>();
500 {
501 ::basegfx::B2IRectangle aRect(lcl_getRect(aPieLabelInfo.xLabelGroupShape));
502 sal_Int32 nPageWidth = m_aPageReferenceSize.Width;
503 sal_Int32 nPageHeight = m_aPageReferenceSize.Height;
504
505 // the data label should be inside the chart area
506 awt::Point aShapePos = aPieLabelInfo.xLabelGroupShape->getPosition();
507 if (aRect.getMinX() < 0)
508 aPieLabelInfo.xLabelGroupShape->setPosition(
509 awt::Point(aShapePos.X - aRect.getMinX(), aShapePos.Y));
510 if (aRect.getMinY() < 0)
511 aPieLabelInfo.xLabelGroupShape->setPosition(
512 awt::Point(aShapePos.X, aShapePos.Y - aRect.getMinY()));
513 if (aRect.getMaxX() > nPageWidth)
514 aPieLabelInfo.xLabelGroupShape->setPosition(
515 awt::Point(aShapePos.X - (aRect.getMaxX() - nPageWidth), aShapePos.Y));
516 if (aRect.getMaxY() > nPageHeight)
517 aPieLabelInfo.xLabelGroupShape->setPosition(
518 awt::Point(aShapePos.X, aShapePos.Y - (aRect.getMaxY() - nPageHeight)));
519
520 if (rSeries.isLabelCustomPos(nPointIndex) && bShowLeaderLine)
521 {
522 sal_Int32 nX1 = aPieLabelInfo.aOuterPosition.getX();
523 sal_Int32 nY1 = aPieLabelInfo.aOuterPosition.getY();
524 sal_Int32 nX2 = nX1;
525 sal_Int32 nY2 = nY1;
526 if (nX1 < aRect.getMinX())
527 nX2 = aRect.getMinX();
528 else if (nX1 > aRect.getMaxX())
529 nX2 = aRect.getMaxX();
530
531 if (nY1 < aRect.getMinY())
532 nY2 = aRect.getMinY();
533 else if (nY1 > aRect.getMaxY())
534 nY2 = aRect.getMaxY();
535
536 sal_Int32 nSquaredDistanceFromOrigin
537 = (nX2 - aOrigin.X) * (nX2 - aOrigin.X) + (nY2 - aOrigin.Y) * (nY2 - aOrigin.Y);
538
539 // tdf#138018 Don't show leader line when custom positioned data label is inside pie chart
540 if (nSquaredDistanceFromOrigin > fSquaredPieRadius)
541 {
542 //when the line is very short compared to the page size don't create one
543 ::basegfx::B2DVector aLength(nX1 - nX2, nY1 - nY2);
544 double fPageDiagonaleLength = std::hypot(nPageWidth, nPageHeight);
545 if ((aLength.getLength() / fPageDiagonaleLength) >= 0.01)
546 {
547 drawing::PointSequenceSequence aPoints{ { {nX1, nY1}, {nX2, nY2} } };
548
549 VLineProperties aVLineProperties;
550 if (aPieLabelInfo.xTextShape.is())
551 {
552 sal_Int32 nColor = 0;
553 aPieLabelInfo.xTextShape->SvxShape::getPropertyValue("CharColor") >>= nColor;
554 //automatic font color does not work for lines -> fallback to black
555 if (nColor != -1)
556 aVLineProperties.Color <<= nColor;
557 }
558 ShapeFactory::createLine2D(xTextTarget, aPoints, &aVLineProperties);
559 }
560 }
561 }
562 }
563
564 aPieLabelInfo.fValue = nVal;
565 aPieLabelInfo.bMovementAllowed = bMovementAllowed;
566 aPieLabelInfo.bMoved = false;
567 aPieLabelInfo.xTextTarget = xTextTarget;
568 aPieLabelInfo.bShowLeaderLine = bShowLeaderLine && !rSeries.isLabelCustomPos(nPointIndex);
569
570 m_aLabelInfoList.push_back(aPieLabelInfo);
571}
572
573void PieChart::addSeries( std::unique_ptr<VDataSeries> pSeries, sal_Int32 /* zSlot */, sal_Int32 /* xSlot */, sal_Int32 /* ySlot */ )
574{
575 VSeriesPlotter::addSeries( std::move(pSeries), 0, -1, 0 );
576}
577
579{
580 return 0.5;
581}
583{
584 if (!std::isnan(m_fMaxOffset))
585 // Value already cached. Use it.
586 return m_fMaxOffset;
587
588 m_fMaxOffset = 0.0;
589 if( m_aZSlots.empty() )
590 return m_fMaxOffset;
591 if( m_aZSlots.front().empty() )
592 return m_fMaxOffset;
593
594 const std::vector< std::unique_ptr<VDataSeries> >& rSeriesList( m_aZSlots.front().front().m_aSeriesVector );
595 if(rSeriesList.empty())
596 return m_fMaxOffset;
597
598 VDataSeries* pSeries = rSeriesList.front().get();
599 rtl::Reference< DataSeries > xSeries( pSeries->getModel() );
600 if( !xSeries.is() )
601 return m_fMaxOffset;
602
603 double fExplodePercentage=0.0;
604 xSeries->getPropertyValue( "Offset") >>= fExplodePercentage;
605 if(fExplodePercentage>m_fMaxOffset)
606 m_fMaxOffset=fExplodePercentage;
607
609 {
610 uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
611 // "AttributedDataPoints"
612 if( xSeries->getFastPropertyValue( PROP_DATASERIES_ATTRIBUTED_DATA_POINTS ) >>= aAttributedDataPointIndexList )
613 {
614 for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
615 {
616 uno::Reference< beans::XPropertySet > xPointProp( pSeries->getPropertiesOfPoint(aAttributedDataPointIndexList[nN]) );
617 if(xPointProp.is())
618 {
619 fExplodePercentage=0.0;
620 xPointProp->getPropertyValue( "Offset") >>= fExplodePercentage;
621 if(fExplodePercentage>m_fMaxOffset)
622 m_fMaxOffset=fExplodePercentage;
623 }
624 }
625 }
626 }
627 return m_fMaxOffset;
628}
630{
631 double fMaxOffset = getMaxOffset();
632 if( !m_aZSlots.empty() && m_bUseRings)
633 return m_aZSlots.front().size()+0.5+fMaxOffset;
634 return 1.5+fMaxOffset;
635}
636double PieChart::getMinimumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32 /* nAxisIndex */ )
637{
638 return 0.0;
639}
640
641double PieChart::getMaximumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32 /* nAxisIndex */ )
642{
643 return 1.0;
644}
645
646bool PieChart::isExpandBorderToIncrementRhythm( sal_Int32 /* nDimensionIndex */ )
647{
648 return false;
649}
650
651bool PieChart::isExpandIfValuesCloseToBorder( sal_Int32 /* nDimensionIndex */ )
652{
653 return false;
654}
655
656bool PieChart::isExpandWideValuesToZero( sal_Int32 /* nDimensionIndex */ )
657{
658 return false;
659}
660
661bool PieChart::isExpandNarrowValuesTowardZero( sal_Int32 /* nDimensionIndex */ )
662{
663 return false;
664}
665
666bool PieChart::isSeparateStackingForDifferentSigns( sal_Int32 /* nDimensionIndex */ )
667{
668 return false;
669}
670
672{
678 if (m_aZSlots.empty())
679 // No series to plot.
680 return;
681
684
687
691
692 OSL_ENSURE(m_xLogicTarget.is() && m_xFinalTarget.is(), "PieChart is not properly initialized.");
693 if (!m_xLogicTarget.is() || !m_xFinalTarget.is())
694 return;
695
702 //check necessary here that different Y axis can not be stacked in the same group? ... hm?
703
709
710 std::vector< VDataSeriesGroup >::iterator aXSlotIter = m_aZSlots.front().begin();
711 const std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = m_aZSlots.front().end();
712
722
726 std::vector< VDataSeriesGroup >::size_type nExplodeableSlot = 0;
727 if( m_pPosHelper->isMathematicalOrientationRadius() && m_bUseRings )
728 nExplodeableSlot = m_aZSlots.front().size()-1;
729
730 m_aLabelInfoList.clear();
731 m_fMaxOffset = std::numeric_limits<double>::quiet_NaN();
732 sal_Int32 n3DRelativeHeight = 100;
733 if ( (m_nDimension==3) && m_xChartTypeModel.is())
734 {
735 try
736 {
737 uno::Any aAny = m_xChartTypeModel->getFastPropertyValue( PROP_PIECHARTTYPE_3DRELATIVEHEIGHT ); // "3DRelativeHeight"
738 aAny >>= n3DRelativeHeight;
739 }
740 catch (const uno::Exception&) { }
741 }
746 for( double fSlotX=0; aXSlotIter != aXSlotEnd && (m_bUseRings||fSlotX<0.5 ); ++aXSlotIter, fSlotX+=1.0 )
747 {
748 ShapeParam aParam;
749
750 std::vector< std::unique_ptr<VDataSeries> >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
751 if(pSeriesList->empty())//there should be only one series in each x slot
752 continue;
753 VDataSeries* pSeries = pSeriesList->front().get();
754 if(!pSeries)
755 continue;
756
757 bool bHasFillColorMapping = pSeries->hasPropertyMapping("FillColor");
758
762 m_pPosHelper->m_fAngleDegreeOffset = pSeries->getStartingAngle();
763
766 sal_Int32 nPointIndex=0;
767 sal_Int32 nPointCount=pSeries->getTotalPointCount();
768 for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ )
769 {
770 double fY = pSeries->getYValue( nPointIndex );
771 if(fY<0.0)
772 {
773 //@todo warn somehow that negative values are treated as positive
774 }
775 if( std::isnan(fY) )
776 continue;
777 aParam.mfLogicYSum += fabs(fY);
778 }
779
780 if (aParam.mfLogicYSum == 0.0)
781 // Total sum of all Y values in this series is zero. Skip the whole series.
782 continue;
783
784 double fLogicYForNextPoint = 0.0;
786 for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ )
787 {
788 double fLogicInnerRadius, fLogicOuterRadius;
789
795 double fOffset = getMaxOffset();
796
798 bool bIsVisible = m_pPosHelper->getInnerAndOuterRadius( fSlotX+1.0, fLogicInnerRadius, fLogicOuterRadius, m_bUseRings, fOffset );
799 if( !bIsVisible )
800 continue;
801
802 aParam.mfDepth = getTransformedDepth() * (n3DRelativeHeight / 100.0);
803
804 rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries, xSeriesTarget);
806 double fLogicYValue = fabs(pSeries->getYValue( nPointIndex ));
807 if( std::isnan(fLogicYValue) )
808 continue;
809 if(fLogicYValue==0.0)//@todo: continue also if the resolution is too small
810 continue;
811 double fLogicYPos = fLogicYForNextPoint;
812 fLogicYForNextPoint += fLogicYValue;
813
814 uno::Reference< beans::XPropertySet > xPointProperties = pSeries->getPropertiesOfPoint( nPointIndex );
815
816 //iterate through all subsystems to create partial points
817 {
818 //logic values on angle axis:
819 double fLogicStartAngleValue = fLogicYPos / aParam.mfLogicYSum;
820 double fLogicEndAngleValue = (fLogicYPos+fLogicYValue) / aParam.mfLogicYSum;
821
825 aParam.mfExplodePercentage = 0.0;
826 bool bDoExplode = ( nExplodeableSlot == static_cast< std::vector< VDataSeriesGroup >::size_type >(fSlotX) );
827 if(bDoExplode) try
828 {
829 xPointProperties->getPropertyValue( "Offset") >>= aParam.mfExplodePercentage;
830 }
831 catch( const uno::Exception& )
832 {
833 TOOLS_WARN_EXCEPTION("chart2", "" );
834 }
835
838 aParam.mfUnitCircleWidthAngleDegree = m_pPosHelper->getWidthAngleDegree( fLogicStartAngleValue, fLogicEndAngleValue );
839 aParam.mfUnitCircleStartAngleDegree = m_pPosHelper->transformToAngleDegree( fLogicStartAngleValue );
840 aParam.mfUnitCircleInnerRadius = m_pPosHelper->transformToRadius( fLogicInnerRadius );
841 aParam.mfUnitCircleOuterRadius = m_pPosHelper->transformToRadius( fLogicOuterRadius );
842
844 aParam.mfLogicZ = -1.0; // For 3D pie chart label position
845
846 // Do concentric explosion if it's a donut chart with more than one series
847 const bool bConcentricExplosion = m_bUseRings && (m_aZSlots.front().size() > 1);
848 rtl::Reference<SvxShape> xPointShape =
850 xSeriesGroupShape_Shapes, xPointProperties, aParam, nPointCount,
851 bConcentricExplosion);
852
854 if (!pSeries->hasPointOwnColor(nPointIndex) && m_xColorScheme.is())
855 {
856 xPointShape->setPropertyValue("FillColor",
857 uno::Any(m_xColorScheme->getColorByIndex( nPointIndex )));
858 }
859
860
861 if(bHasFillColorMapping)
862 {
863 double nPropVal = pSeries->getValueByProperty(nPointIndex, "FillColor");
864 if(!std::isnan(nPropVal))
865 {
866 xPointShape->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>( nPropVal)));
867 }
868 }
869
871 createTextLabelShape(xTextTarget, *pSeries, nPointIndex, aParam);
872
873 if(!bDoExplode)
874 {
875 ShapeFactory::setShapeName( xPointShape
876 , ObjectIdentifier::createPointCID( pSeries->getPointCID_Stub(), nPointIndex ) );
877 }
878 else try
879 {
881
882 double fAngle = aParam.mfUnitCircleStartAngleDegree + aParam.mfUnitCircleWidthAngleDegree/2.0;
883 double fMaxDeltaRadius = aParam.mfUnitCircleOuterRadius-aParam.mfUnitCircleInnerRadius;
884 drawing::Position3D aOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius, aParam.mfLogicZ );
885 drawing::Position3D aNewOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius + fMaxDeltaRadius, aParam.mfLogicZ );
886
887 sal_Int32 nOffsetPercent( static_cast<sal_Int32>(aParam.mfExplodePercentage * 100.0) );
888
890 aOrigin, m_xLogicTarget, m_nDimension ) );
892 aNewOrigin, m_xLogicTarget, m_nDimension ) );
893
894 //enable dragging of piesegments
896 , pSeries->getSeriesParticle()
899 nOffsetPercent, aMinimumPosition, aMaximumPosition )
900 ) );
901
902 ShapeFactory::setShapeName( xPointShape
903 , ObjectIdentifier::createPointCID( aPointCIDStub, nPointIndex ) );
904 }
905 catch( const uno::Exception& )
906 {
907 TOOLS_WARN_EXCEPTION("chart2", "" );
908 }
909 }//next series in x slot (next y slot)
910 }//next category
911 }//next x slot
912}
913
915 : fValue(0.0)
916 , bMovementAllowed(false), bMoved(false)
917 , bShowLeaderLine(false), pPrevious(nullptr)
918 , pNext(nullptr)
919{
920}
921
928bool PieChart::PieLabelInfo::moveAwayFrom( const PieChart::PieLabelInfo* pFix, const awt::Size& rPageSize, bool bMoveHalfWay, bool bMoveClockwise )
929{
930 //return true if the move was successful
931 if(!bMovementAllowed)
932 return false;
933
934 const sal_Int32 nLabelDistanceX = rPageSize.Width/50;
935 const sal_Int32 nLabelDistanceY = rPageSize.Height/50;
936
939 ::basegfx::B2IRectangle aOverlap( lcl_getRect( xLabelGroupShape ) );
940 aOverlap.intersect( lcl_getRect( pFix->xLabelGroupShape ) );
941 if( aOverlap.isEmpty() )
942 return true;
943
944 //TODO: alternative move direction
945
949
956 basegfx::B2IVector aRadiusDirection = aFirstPosition - aOrigin;
957 aRadiusDirection.setLength(1.0);
958 basegfx::B2IVector aTangentialDirection( -aRadiusDirection.getY(), aRadiusDirection.getX() );
959 bool bShiftHorizontal = abs(aTangentialDirection.getX()) > abs(aTangentialDirection.getY());
960 sal_Int32 nShift = bShiftHorizontal ? static_cast<sal_Int32>(aOverlap.getWidth()) : static_cast<sal_Int32>(aOverlap.getHeight());
963 nShift += (bShiftHorizontal ? nLabelDistanceX : nLabelDistanceY);
966 if( bMoveHalfWay )
967 nShift/=2;
970 if(!bMoveClockwise)
971 nShift*=-1;
972 awt::Point aOldPos( xLabelGroupShape->getPosition() );
973 basegfx::B2IVector aNewPos = basegfx::B2IVector( aOldPos.X, aOldPos.Y ) + nShift*aTangentialDirection;
974
977 awt::Point aNewAWTPos( aNewPos.getX(), aNewPos.getY() );
978 if( !lcl_isInsidePage( aNewAWTPos, xLabelGroupShape->getSize(), rPageSize ) )
979 return false;
980
981 xLabelGroupShape->setPosition( aNewAWTPos );
982 bMoved = true;
983
984 return true;
985
995}
996
998{
999 for (auto const& labelInfo : m_aLabelInfoList)
1000 labelInfo.xLabelGroupShape->setPosition(labelInfo.aPreviousPosition);
1001}
1002
1003bool PieChart::detectLabelOverlapsAndMove( const awt::Size& rPageSize )
1004{
1009
1011
1020 bool bOverlapFound = false;
1021 PieLabelInfo* pStart = &(*(m_aLabelInfoList.rbegin()));
1022 PieLabelInfo* pFirstBorder = nullptr;
1023 PieLabelInfo* pSecondBorder = nullptr;
1024 PieLabelInfo* pCurrent = pStart;
1025 do
1026 {
1027 ::basegfx::B2IRectangle aPreviousOverlap( lcl_getRect( pCurrent->xLabelGroupShape ) );
1028 ::basegfx::B2IRectangle aNextOverlap( aPreviousOverlap );
1029 aPreviousOverlap.intersect( lcl_getRect( pCurrent->pPrevious->xLabelGroupShape ) );
1030 aNextOverlap.intersect( lcl_getRect( pCurrent->pNext->xLabelGroupShape ) );
1031
1032 bool bPreviousOverlap = !aPreviousOverlap.isEmpty();
1033 bool bNextOverlap = !aNextOverlap.isEmpty();
1034 if( bPreviousOverlap || bNextOverlap )
1035 bOverlapFound = true;
1036 if( !bPreviousOverlap && bNextOverlap )
1037 {
1038 pFirstBorder = pCurrent;
1039 break;
1040 }
1041 pCurrent = pCurrent->pNext;
1042 }
1043 while( pCurrent != pStart );
1044
1045 if( !bOverlapFound )
1046 return false;
1047
1059 if( pFirstBorder )
1060 {
1061 pCurrent = pFirstBorder;
1062 do
1063 {
1064 ::basegfx::B2IRectangle aPreviousOverlap( lcl_getRect( pCurrent->xLabelGroupShape ) );
1065 ::basegfx::B2IRectangle aNextOverlap( aPreviousOverlap );
1066 aPreviousOverlap.intersect( lcl_getRect( pCurrent->pPrevious->xLabelGroupShape ) );
1067 aNextOverlap.intersect( lcl_getRect( pCurrent->pNext->xLabelGroupShape ) );
1068
1069 if( !aPreviousOverlap.isEmpty() && aNextOverlap.isEmpty() )
1070 {
1071 pSecondBorder = pCurrent;
1072 break;
1073 }
1074 pCurrent = pCurrent->pNext;
1075 }
1076 while( pCurrent != pFirstBorder );
1077 }
1078
1085 if( !pFirstBorder || !pSecondBorder )
1086 {
1087 pFirstBorder = &(*(m_aLabelInfoList.rbegin()));
1088 pSecondBorder = &(*(m_aLabelInfoList.begin()));
1089 }
1090
1093 PieLabelInfo* pCenter = pFirstBorder;
1094 sal_Int32 nOverlapGroupCount = 1;
1095 for( pCurrent = pFirstBorder ;pCurrent != pSecondBorder; pCurrent = pCurrent->pNext )
1096 nOverlapGroupCount++;
1097 sal_Int32 nCenterPos = nOverlapGroupCount/2;
1098 bool bSingleCenter = nOverlapGroupCount%2 != 0;
1099 if( bSingleCenter )
1100 nCenterPos++;
1101 if(nCenterPos>1)
1102 {
1103 pCurrent = pFirstBorder;
1104 while( --nCenterPos )
1105 pCurrent = pCurrent->pNext;
1106 pCenter = pCurrent;
1107 }
1108
1113 pCurrent = pStart;
1114 do
1115 {
1116 pCurrent->aPreviousPosition = pCurrent->xLabelGroupShape->getPosition();
1117 pCurrent = pCurrent->pNext;
1118 }
1119 while( pCurrent != pStart );
1120
1139 bool bAlternativeMoveDirection = false;
1140 if( !tryMoveLabels( pFirstBorder, pSecondBorder, pCenter, bSingleCenter, bAlternativeMoveDirection, rPageSize ) )
1141 tryMoveLabels( pFirstBorder, pSecondBorder, pCenter, bSingleCenter, bAlternativeMoveDirection, rPageSize );
1142
1145 return true;
1146}
1147
1148
1152bool PieChart::tryMoveLabels( PieLabelInfo const * pFirstBorder, PieLabelInfo const * pSecondBorder
1153 , PieLabelInfo* pCenter
1154 , bool bSingleCenter, bool& rbAlternativeMoveDirection, const awt::Size& rPageSize )
1155{
1156
1157 PieLabelInfo* p1 = bSingleCenter ? pCenter->pPrevious : pCenter;
1158 PieLabelInfo* p2 = pCenter->pNext;
1159 //return true when successful
1160
1161 bool bLabelOrderIsAntiClockWise = m_pPosHelper->isMathematicalOrientationAngle();
1162
1169 PieLabelInfo* pCurrent = nullptr;
1170 for( pCurrent = p2 ;pCurrent->pPrevious != pSecondBorder; pCurrent = pCurrent->pNext )
1171 {
1172 PieLabelInfo* pFix = nullptr;
1173 for( pFix = p2->pPrevious ;pFix != pCurrent; pFix = pFix->pNext )
1174 {
1178
1197
1198 if( !pCurrent->moveAwayFrom( pFix, rPageSize, !bSingleCenter && pCurrent == p2, !bLabelOrderIsAntiClockWise ) )
1199 {
1200 if( !rbAlternativeMoveDirection )
1201 {
1202 rbAlternativeMoveDirection = true;
1204 return false;
1205 }
1206 }
1207 }
1208 }
1209
1218
1222
1228
1229 for( pCurrent = p1 ;pCurrent->pNext != pFirstBorder; pCurrent = pCurrent->pPrevious )
1230 {
1231 PieLabelInfo* pFix = nullptr;
1232 for( pFix = p2->pNext ;pFix != pCurrent; pFix = pFix->pPrevious )
1233 {
1234 if( !pCurrent->moveAwayFrom( pFix, rPageSize, false, bLabelOrderIsAntiClockWise ) )
1235 {
1236 if( !rbAlternativeMoveDirection )
1237 {
1238 rbAlternativeMoveDirection = true;
1240 return false;
1241 }
1242 }
1243 }
1244 }
1245 return true;
1246}
1247
1249{
1254 // no need to do anything when we only have one label
1255 if (m_aLabelInfoList.size() < 2)
1256 return;
1257
1259 bool bMoveableFound = false;
1260 for (auto const& labelInfo : m_aLabelInfoList)
1261 {
1262 if(labelInfo.bMovementAllowed)
1263 {
1264 bMoveableFound = true;
1265 break;
1266 }
1267 }
1268 if(!bMoveableFound)
1269 return;
1270
1271 double fPageDiagonaleLength = std::hypot(rPageSize.Width, rPageSize.Height);
1272 if( fPageDiagonaleLength == 0.0 )
1273 return;
1274
1276 auto aIt1 = m_aLabelInfoList.begin();
1277 auto aEnd = m_aLabelInfoList.end();
1278 std::vector< PieLabelInfo >::iterator aIt2 = aIt1;
1279 aIt1->pPrevious = &(*(m_aLabelInfoList.rbegin()));
1280 ++aIt2;
1281 for( ;aIt2!=aEnd; ++aIt1, ++aIt2 )
1282 {
1283 PieLabelInfo& rInfo1( *aIt1 );
1284 PieLabelInfo& rInfo2( *aIt2 );
1285 rInfo1.pNext = &rInfo2;
1286 rInfo2.pPrevious = &rInfo1;
1287 }
1288 aIt1->pNext = &(*(m_aLabelInfoList.begin()));
1289
1291 sal_Int32 nMaxIterations = 50;
1292 while( detectLabelOverlapsAndMove( rPageSize ) && nMaxIterations > 0 )
1293 nMaxIterations--;
1294
1296 VLineProperties aVLineProperties;
1297 for (auto const& labelInfo : m_aLabelInfoList)
1298 {
1299 if( labelInfo.bMoved && labelInfo.bShowLeaderLine )
1300 {
1301 sal_Int32 nX1 = labelInfo.aOuterPosition.getX();
1302 sal_Int32 nY1 = labelInfo.aOuterPosition.getY();
1303 sal_Int32 nX2 = nX1;
1304 sal_Int32 nY2 = nY1;
1305 ::basegfx::B2IRectangle aRect( lcl_getRect( labelInfo.xLabelGroupShape ) );
1306 if( nX1 < aRect.getMinX() )
1307 nX2 = aRect.getMinX();
1308 else if( nX1 > aRect.getMaxX() )
1309 nX2 = aRect.getMaxX();
1310
1311 if( nY1 < aRect.getMinY() )
1312 nY2 = aRect.getMinY();
1313 else if( nY1 > aRect.getMaxY() )
1314 nY2 = aRect.getMaxY();
1315
1316 //when the line is very short compared to the page size don't create one
1317 ::basegfx::B2DVector aLength(nX1-nX2, nY1-nY2);
1318 if( (aLength.getLength()/fPageDiagonaleLength) < 0.01 )
1319 continue;
1320
1321 drawing::PointSequenceSequence aPoints{ { {nX1, nY1}, {nX2, nY2} } };
1322
1323 if( labelInfo.xTextShape.is() )
1324 {
1325 sal_Int32 nColor = 0;
1326 labelInfo.xTextShape->SvxShape::getPropertyValue("CharColor") >>= nColor;
1327 if( nColor != -1 )//automatic font color does not work for lines -> fallback to black
1328 aVLineProperties.Color <<= nColor;
1329 }
1330 ShapeFactory::createLine2D( labelInfo.xTextTarget, aPoints, &aVLineProperties );
1331 }
1332 }
1333}
1334
1335
1412{
1413 SAL_INFO( "chart2.pie.label.bestfit.inside",
1414 "** PieChart::performLabelBestFitInnerPlacement invoked **" );
1415
1416 // get pie slice properties
1417 double fStartAngleDeg = NormAngle360(rShapeParam.mfUnitCircleStartAngleDegree);
1418 double fWidthAngleDeg = rShapeParam.mfUnitCircleWidthAngleDegree;
1419 double fHalfWidthAngleDeg = fWidthAngleDeg / 2.0;
1420 double fBisectingRayAngleDeg = NormAngle360(fStartAngleDeg + fHalfWidthAngleDeg);
1421
1422 // get the middle point of the arc representing the pie slice border
1423 double fLogicZ = rShapeParam.mfLogicZ + 1.0;
1424 awt::Point aMiddleArcPoint = PlottingPositionHelper::transformSceneToScreenPosition(
1425 m_pPosHelper->transformUnitCircleToScene(
1426 fBisectingRayAngleDeg,
1427 rShapeParam.mfUnitCircleOuterRadius,
1428 fLogicZ ),
1430
1431 // compute the pie radius
1432 basegfx::B2IVector aPieCenter = rPieLabelInfo.aOrigin;
1433 basegfx::B2IVector aRadiusVector(
1434 aMiddleArcPoint.X - aPieCenter.getX(),
1435 aMiddleArcPoint.Y - aPieCenter.getY() );
1436 double fSquaredPieRadius = aRadiusVector.scalar(aRadiusVector);
1437 double fPieRadius = sqrt( fSquaredPieRadius );
1438
1439 // the bb is moved as much as possible near to the border of the pie,
1440 // anyway a small offset from the border is present (0.025 * pie radius)
1441 const double fPieBorderOffset = 0.025;
1442 fPieRadius = fPieRadius - fPieRadius * fPieBorderOffset;
1443
1444 SAL_INFO( "chart2.pie.label.bestfit.inside",
1445 " pie sector:" );
1446 SAL_INFO( "chart2.pie.label.bestfit.inside",
1447 " start angle = " << fStartAngleDeg );
1448 SAL_INFO( "chart2.pie.label.bestfit.inside",
1449 " angle width = " << fWidthAngleDeg );
1450 SAL_INFO( "chart2.pie.label.bestfit.inside",
1451 " bisecting ray angle = " << fBisectingRayAngleDeg );
1452 SAL_INFO( "chart2.pie.label.bestfit.inside",
1453 " pie radius = " << fPieRadius );
1454 SAL_INFO( "chart2.pie.label.bestfit.inside",
1455 " pie center = " << rPieLabelInfo.aOrigin );
1456 SAL_INFO( "chart2.pie.label.bestfit.inside",
1457 " middle arc point = (" << aMiddleArcPoint.X << ","
1458 << aMiddleArcPoint.Y << ")" );
1459 SAL_INFO( "chart2.pie.label.bestfit.inside",
1460 " label bounding box:" );
1461 SAL_INFO( "chart2.pie.label.bestfit.inside",
1462 " old anchor point = " << rPieLabelInfo.aFirstPosition );
1463
1464
1465 if( fPieRadius == 0.0 )
1466 return false;
1467
1468 // get label b.b. width and height
1469 ::basegfx::B2IRectangle aBb( lcl_getRect( rPieLabelInfo.xLabelGroupShape ) );
1470 double fLabelWidth = aBb.getWidth();
1471 double fLabelHeight = aBb.getHeight();
1472
1473 // -45 <= fAlphaDeg < 315
1474 double fAlphaDeg = NormAngle360(fBisectingRayAngleDeg + 45) - 45;
1475 double fAlphaRad = basegfx::deg2rad(fAlphaDeg);
1476
1477 // compute nearest edge index
1478 // 0 left
1479 // 1 bottom
1480 // 2 right
1481 // 3 top
1482 int nSectorIndex = floor( (fAlphaDeg + 45) / 45.0 );
1483 int nNearestEdgeIndex = nSectorIndex / 2;
1484
1485 // compute lengths of the nearest edge and of the orthogonal edges
1486 double fNearestEdgeLength = fLabelWidth;
1487 double fOrthogonalEdgeLength = fLabelHeight;
1489 basegfx::Axis2D eOrthogonalAxis = basegfx::Axis2D::Y;
1490 if( nNearestEdgeIndex % 2 == 0 ) // nearest edge is vertical
1491 {
1492 fNearestEdgeLength = fLabelHeight;
1493 fOrthogonalEdgeLength = fLabelWidth;
1494 eAxis = basegfx::Axis2D::Y;
1495 eOrthogonalAxis = basegfx::Axis2D::X;
1496 }
1497
1498 // compute the distance between N and P
1499 // such a distance is piece wise linear respect with alpha:
1500 // given 45k <= alpha < 45(k+1) we have
1501 // when k is even: d(N,P) = (length(e) / 2) * (1 - (alpha - 45k)/45)
1502 // when k is odd: d(N,P) = (length(e) / 2) * (1 - (45(k+1) - alpha)/45)
1503 int nIndex = nSectorIndex -1; // nIndex = -1...6
1504 double fIndexMod2 = (nIndex + 8) % 2; // fIndexMod2 must be non negative
1505 double fSgn = 2.0 * (fIndexMod2 - 0.5); // 0 -> -1, 1 -> 1
1506 double fDistanceNP = (fNearestEdgeLength / 2.0) * (1 + fSgn * ((fAlphaDeg - 45 * (nIndex + fIndexMod2)) / 45.0));
1507 double fDistancePM = fNearestEdgeLength - fDistanceNP;
1508
1509 // compute the length of the diagonal vector d,
1510 // that is the distance between P and F
1511 double fDistancePF = std::hypot(fDistancePM, fOrthogonalEdgeLength);
1512
1513 SAL_INFO( "chart2.pie.label.bestfit.inside",
1514 " width = " << fLabelWidth );
1515 SAL_INFO( "chart2.pie.label.bestfit.inside",
1516 " height = " << fLabelHeight );
1517 SAL_INFO( "chart2.pie.label.bestfit.inside",
1518 " nearest edge index = " << nNearestEdgeIndex );
1519 SAL_INFO( "chart2.pie.label.bestfit.inside",
1520 " alpha = " << fAlphaDeg );
1521 SAL_INFO( "chart2.pie.label.bestfit.inside",
1522 " distance(N,P) = " << fDistanceNP );
1523 SAL_INFO( "chart2.pie.label.bestfit.inside",
1524 " nIndex = " << nIndex );
1525 SAL_INFO( "chart2.pie.label.bestfit.inside",
1526 " fIndexMod2 = " << fIndexMod2 );
1527 SAL_INFO( "chart2.pie.label.bestfit.inside",
1528 " fSgn = " << fSgn );
1529 SAL_INFO( "chart2.pie.label.bestfit.inside",
1530 " distance(P,F) = " << fDistancePF );
1531
1532
1533 // we check that the condition length(d) <= pie radius holds
1534 if (fDistancePF > fPieRadius)
1535 {
1536 return false;
1537 }
1538
1539 // compute beta: the angle of the diagonal vector d,
1540 // that is, the angle in P respect with the triangle PMF;
1541 // since both arguments are non negative the returned value is in [0, PI/2]
1542 double fBetaRad = atan2( fOrthogonalEdgeLength, fDistancePM );
1543
1544 // compute the theta angle, that is the angle in P
1545 // respect with the triangle CFP;
1546 // when the second intersection edge is opposite to the nearest edge,
1547 // theta depends on alpha and beta according to the following relation:
1548 // theta = f(alpha, beta) = s * alpha + 90 * (1 - s * i) + beta
1549 // where i is the nearest edge index and s is the sign of (alpha' - 45),
1550 // with alpha' = (alpha + 45) mod 90;
1551 // when the second intersection edge is adjacent to the nearest edge,
1552 // we have theta = 360 - f(alpha, beta);
1553 // note that in the former case 0 <= f(alpha, beta) <= 180,
1554 // whilst in the latter case 180 <= f(alpha, beta) <= 360;
1555 double fAlphaMod90 = fmod( fAlphaDeg + 45, 90.0 ) - 45;
1556 double fSign = fAlphaMod90 == 0.0
1557 ? 0.0
1558 : ( fAlphaMod90 < 0 ) ? -1.0 : 1.0;
1559 double fThetaRad = fSign * fAlphaRad + M_PI_2 * (1 - fSign * nNearestEdgeIndex) + fBetaRad;
1560 if( fThetaRad > M_PI )
1561 {
1562 fThetaRad = 2 * M_PI - fThetaRad;
1563 }
1564
1565 // compute the length of the positional vector,
1566 // that is the distance between C and P
1567 double fDistanceCP;
1568 // when the bisector ray intersects the b.b. in F we have theta mod 180 == 0
1569 if( fmod(fThetaRad, M_PI) == 0.0 )
1570 {
1571 fDistanceCP = fPieRadius - fDistancePF;
1572 }
1573 else // general case
1574 {
1575 // we can compute d(C,P) by applying some trigonometric formula to
1576 // the triangle CFP : we know length(d) and length(r) = r and we have
1577 // computed the angle in P (theta); so named delta the angle in C and
1578 // gamma the angle in F, by the relation:
1579 //
1580 // r d(P,F) d(C,P)
1581 // --------- = --------- = ---------
1582 // sin theta sin delta sin gamma
1583 //
1584 // we get the wanted distance
1585 double fSinTheta = sin( fThetaRad );
1586 double fSinDelta = fDistancePF * fSinTheta / fPieRadius;
1587 double fDeltaRad = asin( fSinDelta );
1588 double fGammaRad = M_PI - (fThetaRad + fDeltaRad);
1589 double fSinGamma = sin( fGammaRad );
1590 fDistanceCP = fPieRadius * fSinGamma / fSinTheta;
1591 }
1592
1593 // define the positional vector
1594 basegfx::B2DVector aPositionalVector( cos(fAlphaRad), sin(fAlphaRad) );
1595 aPositionalVector.setLength(fDistanceCP);
1596
1597 // we define a direction vector in order to know
1598 // in which quadrant we are working
1600 if( 90 <= fBisectingRayAngleDeg && fBisectingRayAngleDeg < 270 )
1601 {
1602 aDirection.setX(-1.0);
1603 }
1604 if( fBisectingRayAngleDeg >= 180 )
1605 {
1606 aDirection.setY(-1.0);
1607 }
1608
1609 // compute vertices N, M and G respect with pie center C
1610 basegfx::B2DVector aNearestVertex(aPositionalVector);
1611 aNearestVertex.set(eAxis, aNearestVertex.get(eAxis) - aDirection.get(eAxis) * fDistanceNP);
1612 basegfx::B2DVector aVertexM(aNearestVertex);
1613 aVertexM.set(eAxis, aVertexM.get(eAxis) + aDirection.get(eAxis) * fNearestEdgeLength);
1614 basegfx::B2DVector aVertexG(aNearestVertex);
1615 aVertexG.set(eOrthogonalAxis, aVertexG.get(eOrthogonalAxis) + aDirection.get(eOrthogonalAxis) * fOrthogonalEdgeLength);
1616
1617 SAL_INFO( "chart2.pie.label.bestfit.inside",
1618 " beta = " << basegfx::rad2deg(fBetaRad) );
1619 SAL_INFO( "chart2.pie.label.bestfit.inside",
1620 " theta = " << basegfx::rad2deg(fThetaRad) );
1621 SAL_INFO( "chart2.pie.label.bestfit.inside",
1622 " fAlphaMod90 = " << fAlphaMod90 );
1623 SAL_INFO( "chart2.pie.label.bestfit.inside",
1624 " fSign = " << fSign );
1625 SAL_INFO( "chart2.pie.label.bestfit.inside",
1626 " distance(C,P) = " << fDistanceCP );
1627 SAL_INFO( "chart2.pie.label.bestfit.inside",
1628 " direction vector = " << aDirection );
1629 SAL_INFO( "chart2.pie.label.bestfit.inside",
1630 " N = " << aNearestVertex );
1631 SAL_INFO( "chart2.pie.label.bestfit.inside",
1632 " M = " << aVertexM );
1633 SAL_INFO( "chart2.pie.label.bestfit.inside",
1634 " G = " << aVertexG );
1635
1636 // in order to be able to place the label inside the pie slice we need
1637 // to check that each angle between s and the ray starting from C and
1638 // passing through a b.b. vertex is less than half width of the pie slice;
1639 // when the nearest edge e crosses a Cartesian axis it is sufficient
1640 // to test only the vertices belonging to e, else we need to test
1641 // the 2 vertices that aren't either N or F. Note that if a b.b. edge
1642 // crosses a Cartesian axis then it is the nearest edge to C
1643
1644 // check the angle between CP and CM
1645 double fAngleRad = aPositionalVector.angle(aVertexM);
1646 double fAngleDeg = NormAngle360(basegfx::rad2deg(fAngleRad));
1647 if( fAngleDeg > 180 ) // in case the wrong angle has been computed
1648 fAngleDeg = 360 - fAngleDeg;
1649 SAL_INFO( "chart2.pie.label.bestfit.inside",
1650 " angle between CP and CM: " << fAngleDeg );
1651 if( fAngleDeg > fHalfWidthAngleDeg )
1652 {
1653 return false;
1654 }
1655
1656 if( ( aNearestVertex.get(eAxis) >= 0 && aVertexM.get(eAxis) <= 0 )
1657 || ( aNearestVertex.get(eAxis) <= 0 && aVertexM.get(eAxis) >= 0 ) )
1658 {
1659 // check the angle between CP and CN
1660 fAngleRad = aPositionalVector.angle(aNearestVertex);
1661 fAngleDeg = NormAngle360(basegfx::rad2deg(fAngleRad));
1662 if( fAngleDeg > 180 ) // in case the wrong angle has been computed
1663 fAngleDeg = 360 - fAngleDeg;
1664 SAL_INFO( "chart2.pie.label.bestfit.inside",
1665 " angle between CP and CN: " << fAngleDeg );
1666 if( fAngleDeg > fHalfWidthAngleDeg )
1667 {
1668 return false;
1669 }
1670 }
1671 else
1672 {
1673 // check the angle between CP and CG
1674 fAngleRad = aPositionalVector.angle(aVertexG);
1675 fAngleDeg = NormAngle360(basegfx::rad2deg(fAngleRad));
1676 if( fAngleDeg > 180 ) // in case the wrong angle has been computed
1677 fAngleDeg = 360 - fAngleDeg;
1678 SAL_INFO( "chart2.pie.label.bestfit.inside",
1679 " angle between CP and CG: " << fAngleDeg );
1680 if( fAngleDeg > fHalfWidthAngleDeg )
1681 {
1682 return false;
1683 }
1684 }
1685
1686 // compute the b.b. center respect with the pie center
1687 basegfx::B2DVector aBBCenter(aNearestVertex);
1688 aBBCenter.set(eAxis, aBBCenter.get(eAxis) + aDirection.get(eAxis) * fNearestEdgeLength / 2);
1689 aBBCenter.set(eOrthogonalAxis, aBBCenter.get(eOrthogonalAxis) + aDirection.get(eOrthogonalAxis) * fOrthogonalEdgeLength / 2);
1690
1691 // compute the b.b. anchor point
1692 basegfx::B2IVector aNewAnchorPoint = aPieCenter;
1693 aNewAnchorPoint.setX(aNewAnchorPoint.getX() + floor(aBBCenter.getX()));
1694 aNewAnchorPoint.setY(aNewAnchorPoint.getY() - floor(aBBCenter.getY())); // the Y axis on the screen points downward
1695
1696 // compute the translation vector for moving the label from the current
1697 // screen position to the new one
1698 basegfx::B2IVector aTranslationVector = aNewAnchorPoint - rPieLabelInfo.aFirstPosition;
1699
1700 // compute the new screen position and move the label
1701 // XShape::getPosition returns the top left vertex of the b.b. of the shape
1702 awt::Point aOldPos( rPieLabelInfo.xLabelGroupShape->getPosition() );
1703 awt::Point aNewPos( aOldPos.X + aTranslationVector.getX(),
1704 aOldPos.Y + aTranslationVector.getY() );
1705 rPieLabelInfo.xLabelGroupShape->setPosition(aNewPos);
1706
1707 SAL_INFO( "chart2.pie.label.bestfit.inside",
1708 " center = " << aBBCenter );
1709 SAL_INFO( "chart2.pie.label.bestfit.inside",
1710 " new anchor point = " << aNewAnchorPoint );
1711 SAL_INFO( "chart2.pie.label.bestfit.inside",
1712 " translation vector = " << aTranslationVector );
1713 SAL_INFO( "chart2.pie.label.bestfit.inside",
1714 " old position = (" << aOldPos.X << "," << aOldPos.Y << ")" );
1715 SAL_INFO( "chart2.pie.label.bestfit.inside",
1716 " new position = (" << aNewPos.X << "," << aNewPos.Y << ")" );
1717
1718 return true;
1719}
1720
1721} //namespace chart
1722
1723/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double angle(const B2DVector &rVec) const
B2DVector & setLength(double fLen)
double getLength() const
double scalar(const B2IVector &rVec) const
B2IVector & setLength(double fLen)
TYPE getMaxX() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
void intersect(const Range2D &rRange)
bool isEmpty() const
TYPE getHeight() const
TYPE getX() const
void setY(TYPE fY)
void set(Axis2D eAxis, TYPE fValue)
TYPE getY() const
double get(Axis2D eAxis)
void setX(TYPE fX)
css::awt::Point transformSceneToScreenPosition(const css::drawing::Position3D &rScenePosition3D) const
static OUString createPieSegmentDragParameterString(sal_Int32 nOffsetPercent, const css::awt::Point &rMinimumPosition, const css::awt::Point &rMaximumPosition)
static OUString createSeriesSubObjectStub(ObjectType eSubObjectType, std::u16string_view rSeriesParticle, std::u16string_view rDragMethodServiceName=std::u16string_view(), std::u16string_view rDragParameterString=std::u16string_view())
static OUString createPointCID(std::u16string_view rPointCID_Stub, sal_Int32 nIndex)
static const OUString & getPieSegmentDragMethodServiceName()
virtual bool isSeparateStackingForDifferentSigns(sal_Int32 nDimensionIndex) override
Definition: PieChart.cxx:666
virtual double getMinimumX() override
Definition: PieChart.cxx:578
bool detectLabelOverlapsAndMove(const css::awt::Size &rPageSize)
Definition: PieChart.cxx:1003
virtual void createShapes() override
This method creates all shapes needed for representing the pie chart.
Definition: PieChart.cxx:671
virtual bool shouldSnapRectToUsedArea() override
Definition: PieChart.cxx:244
std::vector< PieLabelInfo > m_aLabelInfoList
Definition: PieChart.hxx:139
rtl::Reference< SvxShape > createDataPoint(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const css::uno::Reference< css::beans::XPropertySet > &xObjectProperties, const ShapeParam &rParam, const sal_Int32 nPointCount, const bool bConcentricExplosion)
Definition: PieChart.cxx:249
bool performLabelBestFitInnerPlacement(ShapeParam &rShapeParam, PieLabelInfo const &rPieLabelInfo)
Handle the placement of the label in the best fit case: the routine try to place the label inside the...
Definition: PieChart.cxx:1411
virtual ~PieChart() override
Definition: PieChart.cxx:227
virtual bool isExpandBorderToIncrementRhythm(sal_Int32 nDimensionIndex) override
Definition: PieChart.cxx:646
void resetLabelPositionsToPreviousState()
Definition: PieChart.cxx:997
virtual bool isExpandWideValuesToZero(sal_Int32 nDimensionIndex) override
Definition: PieChart.cxx:656
void createTextLabelShape(const rtl::Reference< SvxShapeGroupAnyD > &xTextTarget, VDataSeries &rSeries, sal_Int32 nPointIndex, ShapeParam &rParam)
This method creates a text shape for a label of a data point.
Definition: PieChart.cxx:316
virtual double getMaximumYInRange(double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex) override
Definition: PieChart.cxx:641
std::unique_ptr< PiePositionHelper > m_pPosHelper
Definition: PieChart.hxx:114
double m_fMaxOffset
Definition: PieChart.hxx:141
virtual void addSeries(std::unique_ptr< VDataSeries > pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot) override
A new series can be positioned relative to other series in a chart.
Definition: PieChart.cxx:573
bool tryMoveLabels(PieLabelInfo const *pFirstBorder, PieLabelInfo const *pSecondBorder, PieLabelInfo *pCenter, bool bSingleCenter, bool &rbAlternativeMoveDirection, const css::awt::Size &rPageSize)
Try to remove all overlaps that occur in the list of labels going from pFirstBorder to pSecondBorder
Definition: PieChart.cxx:1152
virtual double getMaximumX() override
Definition: PieChart.cxx:629
virtual double getMinimumYInRange(double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex) override
Definition: PieChart.cxx:636
virtual bool isExpandNarrowValuesTowardZero(sal_Int32 nDimensionIndex) override
Definition: PieChart.cxx:661
virtual css::drawing::Direction3D getPreferredDiagramAspectRatio() const override
a value <= 0 for a directions means that this direction can be stretched arbitrary
Definition: PieChart.cxx:237
virtual void rearrangeLabelToAvoidOverlapIfRequested(const css::awt::Size &rPageSize) override
Definition: PieChart.cxx:1248
virtual bool isExpandIfValuesCloseToBorder(sal_Int32 nDimensionIndex) override
Definition: PieChart.cxx:651
double getMaxOffset()
This method sets m_fMaxOffset to the maximum Offset property and returns it.
Definition: PieChart.cxx:582
virtual void setScales(std::vector< ExplicitScaleData > &&rScales, bool bSwapXAndYAxis) override
Definition: PieChart.cxx:231
bool m_bSizeExcludesLabelsAndExplodedSegments
Definition: PieChart.hxx:116
PieChart()=delete
bool getInnerAndOuterRadius(double fCategoryX, double &fLogicInnerRadius, double &fLogicOuterRadius, bool bUseRings, double fMaxOffset) const
Compute the outer and the inner radius for the current ring (not for the whole donut!...
Definition: PieChart.cxx:159
PiePositionHelper(double fAngleDegreeOffset)
Definition: PieChart.cxx:140
rtl::Reference< SvxShapeGroupAnyD > m_xLogicTarget
Definition: PlotterBase.hxx:69
PlottingPositionHelper * m_pPosHelper
Definition: PlotterBase.hxx:75
rtl::Reference< SvxShapeGroupAnyD > m_xFinalTarget
Definition: PlotterBase.hxx:70
const sal_Int32 m_nDimension
Definition: PlotterBase.hxx:73
rtl::Reference< SvxShapeGroupAnyD > createGroupShape(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const OUString &rName=OUString())
Definition: PlotterBase.cxx:71
static css::awt::Point transformSceneToScreenPosition(const css::drawing::Position3D &rScenePosition3D, const rtl::Reference< SvxShapeGroupAnyD > &xSceneTarget, sal_Int32 nDimensionCount)
css::awt::Point getLabelScreenPositionAndAlignmentForUnitCircleValues(LabelAlignment &rAlignment, sal_Int32 nLabelPlacement, double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree, double fUnitCircleInnerRadius, double fUnitCircleOuterRadius, double fLogicZ, sal_Int32 nScreenValueOffsetInRadiusDirection) const
Calculate the anchor point position for a text label.
double m_fAngleDegreeOffset
Offset for angle axis in real degree.
double m_fRadiusOffset
m_bSwapXAndY (inherited): by default the X axis (scale[0]) represents the angle axis and the Y axis (...
static const tPropertyNameMap & getPropertyNameMapForFilledSeriesProperties()
static void setMappedProperties(const css::uno::Reference< css::beans::XPropertySet > &xTarget, const css::uno::Reference< css::beans::XPropertySet > &xSource, const tPropertyNameMap &rMap)
static void setShapeName(const rtl::Reference< SvxShape > &xShape, const OUString &rName)
static rtl::Reference< SvxShapePolyPolygon > createPieSegment2D(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree, double fUnitCircleInnerRadius, double fUnitCircleOuterRadius, const css::drawing::Direction3D &rOffset, const css::drawing::HomogenMatrix &rUnitCircleToScene)
static rtl::Reference< Svx3DExtrudeObject > createPieSegment(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree, double fUnitCircleInnerRadius, double fUnitCircleOuterRadius, const css::drawing::Direction3D &rOffset, const css::drawing::HomogenMatrix &rUnitCircleToScene, double fDepth)
static rtl::Reference< SvxShapePolyPolygon > createLine2D(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const css::drawing::PointSequenceSequence &rPoints, const VLineProperties *pLineProperties=nullptr)
static rtl::Reference< SvxShapeGroup > createGroup2D(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, const OUString &aName=OUString())
const OUString & getPointCID_Stub() const
css::uno::Reference< css::beans::XPropertySet > getPropertiesOfPoint(sal_Int32 index) const
double getYValue(sal_Int32 index) const
double getValueByProperty(sal_Int32 index, const OUString &rPropName) const
css::chart2::DataPointLabel * getDataPointLabelIfLabel(sal_Int32 index) const
sal_Int32 getStartingAngle() const
const rtl::Reference<::chart::DataSeries > & getModel() const
bool hasPropertyMapping(const OUString &rPropName) const
sal_Int32 getLabelPlacement(sal_Int32 nPointIndex, const rtl::Reference<::chart::ChartType > &xChartType, bool bSwapXAndY) const
sal_Int32 getTotalPointCount() const
Definition: VDataSeries.hxx:79
bool hasPointOwnColor(sal_Int32 index) const
bool isLabelCustomPos(sal_Int32 nPointIndex) const
const OUString & getSeriesParticle() const
double getTransformedDepth() const
rtl::Reference< SvxShapeText > createDataLabel(const rtl::Reference< SvxShapeGroupAnyD > &xTarget, VDataSeries &rDataSeries, sal_Int32 nPointIndex, double fValue, double fSumValue, const css::awt::Point &rScreenPosition2D, LabelAlignment eAlignment, sal_Int32 nOffset=0, sal_Int32 nTextWidth=0)
This method creates a text shape for a label related to a data point and append it to the root text s...
css::awt::Size m_aPageReferenceSize
css::uno::Reference< css::chart2::XColorScheme > m_xColorScheme
PlottingPositionHelper * m_pMainPosHelper
rtl::Reference< SvxShapeGroupAnyD > getSeriesGroupShape(VDataSeries *pDataSeries, const rtl::Reference< SvxShapeGroupAnyD > &xTarget)
std::vector< std::vector< VDataSeriesGroup > > m_aZSlots
rtl::Reference< ::chart::ChartType > m_xChartTypeModel
basegfx::B2IRectangle m_aAvailableOuterRect
virtual void addSeries(std::unique_ptr< VDataSeries > pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot)
A new series can be positioned relative to other series in a chart.
#define TOOLS_WARN_EXCEPTION(area, stream)
Reference< XInterface > xTarget
T NormAngle360(T angle)
sal_Int32 nIndex
#define SAL_INFO(area, stream)
if(aStr !=aBuf) UpdateName_Impl(m_xFollowLb.get()
constexpr double rad2deg(double v)
constexpr double deg2rad(double v)
OOO_DLLPUBLIC_CHARTTOOLS::basegfx::B2IRectangle makeRectangle(const css::awt::Point &rPosition, const css::awt::Size &rSize)
@ OBJECTTYPE_DATA_POINT
@ PROP_PIECHARTTYPE_3DRELATIVEHEIGHT
Definition: ChartType.hxx:44
@ PROP_PIECHARTTYPE_USE_RINGS
Definition: ChartType.hxx:43
@ LABEL_ALIGN_CENTER
OOO_DLLPUBLIC_CHARTTOOLS css::drawing::HomogenMatrix B3DHomMatrixToHomogenMatrix(const ::basegfx::B3DHomMatrix &rM)
diverse methods for class conversions; e.g.
SwNodeOffset abs(const SwNodeOffset &a)
css::awt::Point aPreviousPosition
Definition: PieChart.hxx:136
::basegfx::B2IVector aFirstPosition
Definition: PieChart.hxx:126
::basegfx::B2IVector aOuterPosition
Definition: PieChart.hxx:127
rtl::Reference< SvxShapeGroupAnyD > xLabelGroupShape
Definition: PieChart.hxx:125
bool moveAwayFrom(const PieLabelInfo *pFix, const css::awt::Size &rPageSize, bool bMoveHalfWay, bool bMoveClockwise)
In case this label and the passed label overlap the routine moves this label in order to fix the issu...
Definition: PieChart.cxx:928
rtl::Reference< SvxShapeGroupAnyD > xTextTarget
Definition: PieChart.hxx:133
rtl::Reference< SvxShapeText > xTextShape
Definition: PieChart.hxx:124
::basegfx::B2IVector aOrigin
Definition: PieChart.hxx:128
double mfUnitCircleOuterRadius
the normalized outer radius of the ring the slice belongs to.
Definition: PieChart.cxx:62
double mfUnitCircleStartAngleDegree
the start angle of the slice
Definition: PieChart.cxx:54
double mfUnitCircleWidthAngleDegree
the angle width of the slice
Definition: PieChart.cxx:58
double mfLogicZ
for 3D pie chart: label z coordinate
Definition: PieChart.cxx:88
double mfLogicYSum
sum of all Y values in a single series
Definition: PieChart.cxx:84
double mfUnitCircleInnerRadius
the normalized inner radius of the ring the slice belongs to
Definition: PieChart.cxx:66
double mfDepth
for 3D pie chart: height
Definition: PieChart.cxx:92
double mfExplodePercentage
relative distance offset of a slice from the pie center; this parameter is used for instance when the...
Definition: PieChart.cxx:80
css::drawing::Direction3D aDirection
unsigned char sal_Bool