summaryrefslogtreecommitdiff
path: root/chart2
diff options
context:
space:
mode:
authorMarco Cecchetti <marco.cecchetti@collabora.com>2015-02-09 13:36:49 +0100
committerMarco Cecchetti <marco.cecchetti@collabora.com>2015-02-09 13:42:19 +0100
commit1a595052b205f8505776699f61c1a0b89bc42380 (patch)
tree524a7942fc4002aa6e93ea21fb2705e484912a66 /chart2
parentab50e4f8e5b34b15e4f6a338fb7326035b7d3180 (diff)
Added doc notes for classes and methods used for pie charts.
Diffstat (limited to 'chart2')
-rw-r--r--chart2/source/view/charttypes/PieChart.cxx58
-rw-r--r--chart2/source/view/charttypes/PieChart.hxx12
-rw-r--r--chart2/source/view/charttypes/VSeriesPlotter.cxx21
-rw-r--r--chart2/source/view/inc/PlotterBase.hxx4
-rw-r--r--chart2/source/view/inc/PlottingPositionHelper.hxx35
-rw-r--r--chart2/source/view/inc/PolarLabelPositionHelper.hxx11
-rw-r--r--chart2/source/view/inc/VSeriesPlotter.hxx35
-rw-r--r--chart2/source/view/main/PlottingPositionHelper.cxx73
8 files changed, 245 insertions, 4 deletions
diff --git a/chart2/source/view/charttypes/PieChart.cxx b/chart2/source/view/charttypes/PieChart.cxx
index 3edf2af46d62..5c29b3606c77 100644
--- a/chart2/source/view/charttypes/PieChart.cxx
+++ b/chart2/source/view/charttypes/PieChart.cxx
@@ -41,13 +41,38 @@ namespace chart {
struct PieChart::ShapeParam
{
+ // the start angle of the slice
double mfUnitCircleStartAngleDegree;
+
+ // the angle width of the slice
double mfUnitCircleWidthAngleDegree;
+
+ // the normalized outer radius of the ring the slice belongs to.
double mfUnitCircleOuterRadius;
+
+ // the normalized inner radius of the ring the slice belongs to
double mfUnitCircleInnerRadius;
+
+ // relative distance offset of a slice from the pie center;
+ // this parameter is used for instance when the user performs manual
+ // dragging of a slice (the drag operation is possible only for slices that
+ // belong to the outer ring and only along the ray bisecting the slice);
+ // the value for the given entry in the data series is obtained by the
+ // `Offset` property attached to each entry; note that the value
+ // provided by the `Offset` property is used both as a logical value in
+ // `PiePositionHelper::getInnerAndOuterRadius` and as a percentage value in
+ // the `PieChart::createDataPoint` and `PieChart::createTextLabelShape`
+ // methods; since the logical height of a ring is always 1, this duality
+ // does not cause any incorrect behavior.
double mfExplodePercentage;
- double mfLogicYSum; // sum of all Y values in a single series.
+
+ // sum of all Y values in a single series.
+ double mfLogicYSum;
+
+ // for 3D pie chart: label z coordinate
double mfLogicZ;
+
+ // for 3D pie chart: height
double mfDepth;
ShapeParam() :
@@ -86,6 +111,18 @@ PiePositionHelper::~PiePositionHelper()
{
}
+/* Compute the outer and the inner radius for the current ring (not for the
+ * whole donut!), in general it is:
+ * inner_radius = (ring_index + 1) - 0.5 + max_offset,
+ * outer_radius = (ring_index + 1) + 0.5 + max_offset.
+ * When orientation for the radius axis is reversed these values are swapped.
+ * (Indeed the the orientation for the radius axis is always reversed!
+ * See `PieChartTypeTemplate::adaptScales`.)
+ * The maximum relative offset (see notes for P`ieChart::getMaxOffset`) is
+ * added to both the inner and the outer radius.
+ * It returns true if the ring is visible (that is not out of the radius
+ * axis scale range).
+ */
bool PiePositionHelper::getInnerAndOuterRadius( double fCategoryX
, double& fLogicInnerRadius, double& fLogicOuterRadius
, bool bUseRings, double fMaxOffset ) const
@@ -230,6 +267,10 @@ void PieChart::createTextLabelShape(
// There is no text label for this data point. Nothing to do.
return;
+ //by using the `mfExplodePercentage` parameter a normalized offset is added
+ // to both normalized radii. (See notes for
+ // `PolarPlottingPositionHelper::transformToRadius`, especially example 3,
+ // and related comments).
if (!rtl::math::approxEqual(rParam.mfExplodePercentage, 0.0))
{
double fExplodeOffset = (rParam.mfUnitCircleOuterRadius-rParam.mfUnitCircleInnerRadius)*rParam.mfExplodePercentage;
@@ -237,6 +278,8 @@ void PieChart::createTextLabelShape(
rParam.mfUnitCircleOuterRadius += fExplodeOffset;
}
+ //get the required label placement type. Available placements are
+ //`AVOID_OVERLAP`, `CENTER`, `OUTSIDE` and `INSIDE`.
sal_Int32 nLabelPlacement = rSeries.getLabelPlacement(
nPointIndex, m_xChartTypeModel, m_nDimension, m_pPosHelper->isSwapXAndY());
@@ -249,6 +292,12 @@ void PieChart::createTextLabelShape(
// does.
nLabelPlacement = ::com::sun::star::chart::DataLabelPlacement::CENTER;
+ //for `OUTSIDE` (`INSIDE`) label placements an offset of 150 (-150), in the
+ //radius direction, is added to the final screen position of the label
+ //anchor point. This is required in order to ensure that the label is
+ //completely outside (inside) the related slice. Indeed this value should
+ //depend on the font height.
+ Pay attention: 150 is not a big offset, in fact the screen position coordinates for label anchor points are in the 10000-20000 range, hence these are coordinates of a virtual screen and 150 is a small value.
LabelAlignment eAlignment(LABEL_ALIGN_CENTER);
sal_Int32 nScreenValueOffsetInRadiusDirection = 0 ;
if( nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::OUTSIDE )
@@ -256,12 +305,16 @@ void PieChart::createTextLabelShape(
else if( nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::INSIDE )
nScreenValueOffsetInRadiusDirection = (3!=m_nDimension) ? -150 : 0;//todo maybe calculate this font height dependent
+ //the scene position of the label anchor point is calculated (see notes for
+ //`PolarLabelPositionHelper::getLabelScreenPositionAndAlignmentForUnitCircleValues`),
+ //and immediately transformed into the screen position.
PolarLabelPositionHelper aPolarPosHelper(m_pPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory);
awt::Point aScreenPosition2D(
aPolarPosHelper.getLabelScreenPositionAndAlignmentForUnitCircleValues(eAlignment, nLabelPlacement
, rParam.mfUnitCircleStartAngleDegree, rParam.mfUnitCircleWidthAngleDegree
, rParam.mfUnitCircleInnerRadius, rParam.mfUnitCircleOuterRadius, rParam.mfLogicZ+0.5, 0 ));
+ //the screen position of the pie/donut center is calculated.
PieLabelInfo aPieLabelInfo;
aPieLabelInfo.aFirstPosition = basegfx::B2IVector( aScreenPosition2D.X, aScreenPosition2D.Y );
awt::Point aOrigin( aPolarPosHelper.transformSceneToScreenPosition( m_pPosHelper->transformUnitCircleToScene( 0.0, 0.0, rParam.mfLogicZ+1.0 ) ) );
@@ -276,10 +329,13 @@ void PieChart::createTextLabelShape(
aScreenPosition2D.Y += aDirection.getY();
}
+ //the text shape for the label is created
double nVal = rSeries.getYValue(nPointIndex);
aPieLabelInfo.xTextShape = createDataLabel(
xTextTarget, rSeries, nPointIndex, nVal, rParam.mfLogicYSum, aScreenPosition2D, eAlignment);
+ //a new `PieLabelInfo` instance is initialized with all the info related to
+ //the current label in order to simplify later label position rearrangement.
uno::Reference< container::XChild > xChild( aPieLabelInfo.xTextShape, uno::UNO_QUERY );
if( xChild.is() )
aPieLabelInfo.xLabelGroupShape = uno::Reference<drawing::XShape>( xChild->getParent(), uno::UNO_QUERY );
diff --git a/chart2/source/view/charttypes/PieChart.hxx b/chart2/source/view/charttypes/PieChart.hxx
index 2b88b1a0ca56..2929f963aa58 100644
--- a/chart2/source/view/charttypes/PieChart.hxx
+++ b/chart2/source/view/charttypes/PieChart.hxx
@@ -75,6 +75,18 @@ private: //methods
const css::uno::Reference<css::drawing::XShapes>& xTextTarget,
VDataSeries& rSeries, sal_Int32 nPointIndex, ShapeParam& rParam );
+ /** This method sets `m_fMaxOffset` to the maximum `Offset` property and
+ * returns it. There is a `Offset` property for each entry in a data
+ * series, moreover there exists a shared `Offset` property attached to
+ * the whole data series. The `Offset` property represents the
+ * relative distance offset of a slice from the pie center.
+ * The shared property is used for exploded pie chart, while the property
+ * attached to single data series entries is used for manual dragging of
+ * a slice.
+ * `m_fMaxOffset` is used by `PiePositionHelper::getInnerAndOuterRadius`.
+ * Note that only the `Offset` properties of the first (x slot) data series
+ * and its entries are utilized for computing the maximum offset.
+ */
double getMaxOffset();
bool detectLabelOverlapsAndMove(const ::com::sun::star::awt::Size& rPageSize);//returns true when there might be more to do
void resetLabelPositionsToPreviousState();
diff --git a/chart2/source/view/charttypes/VSeriesPlotter.cxx b/chart2/source/view/charttypes/VSeriesPlotter.cxx
index 8acf4273e1d8..5fdd37bab82d 100644
--- a/chart2/source/view/charttypes/VSeriesPlotter.cxx
+++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx
@@ -449,6 +449,11 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
//prepare legend symbol
+ // get the font size for the label through the "CharHeight" property
+ // attached to the passed data series entry.
+ // (By tracing font height values it results that for pie chart the
+ // font size is not the same for all labels, but here no font size
+ // modification occurs).
float fViewFontSize( 10.0 );
{
uno::Reference< beans::XPropertySet > xProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) );
@@ -457,6 +462,9 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
// pt -> 1/100th mm
fViewFontSize *= (2540.0f / 72.0f);
}
+
+ // the font height is used for computing the size of an optional legend
+ // symbol to be prepended to the text label.
Reference< drawing::XShape > xSymbol;
if(pLabel->ShowLegendSymbol)
{
@@ -485,6 +493,8 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
if(xPointProps.is())
{
xPointProps->getPropertyValue( "LabelSeparator" ) >>= aSeparator;
+ // Extract the optional text rotation through the
+ // "TextRotation" property attached to the passed data point.
xPointProps->getPropertyValue( "TextRotation" ) >>= fRotationDegrees;
}
}
@@ -492,6 +502,11 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
{
ASSERT_EXCEPTION( e );
}
+
+ // check if data series entry percent value and absolute value have to
+ // be appended to the text label, and what should be the separator
+ // character (comma, space, new line). In case it is a new line we get
+ // a multi-line label.
bool bMultiLineLabel = aSeparator == "\n";
sal_Int32 nLineCountForSymbolsize = 0;
{
@@ -543,6 +558,8 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
tAnySequence* pPropValues;
if( !rDataSeries.getTextLabelMultiPropertyLists( nPointIndex, pPropNames, pPropValues ) )
return xTextShape;
+
+ // set text alignment for the text shape to be created.
LabelPositionHelper::changeTextAdjustment( *pPropValues, *pPropNames, eAlignment );
//create text shape
@@ -553,6 +570,8 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
if( !xTextShape.is() )
return xTextShape;
+ // in case text is rotated, the transformation property of the text
+ // shape is modified.
const awt::Point aUnrotatedTextPos( xTextShape->getPosition() );
if( fRotationDegrees != 0.0 )
{
@@ -563,6 +582,8 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
LabelPositionHelper::correctPositionForRotation( xTextShape, eAlignment, fRotationDegrees, true /*bRotateAroundCenter*/ );
}
+ // in case legend symbol has to be displayed, text shape position is
+ // slightly changed.
if( xSymbol.is() )
{
const awt::Point aOldTextPos( xTextShape->getPosition() );
diff --git a/chart2/source/view/inc/PlotterBase.hxx b/chart2/source/view/inc/PlotterBase.hxx
index 887f8ca96ae4..b7515c49860c 100644
--- a/chart2/source/view/inc/PlotterBase.hxx
+++ b/chart2/source/view/inc/PlotterBase.hxx
@@ -39,6 +39,10 @@ namespace chart
class AbstractShapeFactory;
class PlottingPositionHelper;
+/** This class provides methods for setting axis scales and for performing
+ * scene to screen transformations. It is used as the base class for all
+ * plotter classes.
+ */
class PlotterBase
{
public:
diff --git a/chart2/source/view/inc/PlottingPositionHelper.hxx b/chart2/source/view/inc/PlottingPositionHelper.hxx
index e913cdd0ed51..969d886a3483 100644
--- a/chart2/source/view/inc/PlottingPositionHelper.hxx
+++ b/chart2/source/view/inc/PlottingPositionHelper.hxx
@@ -160,10 +160,25 @@ public:
virtual ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >
getTransformationScaledLogicToScene() const SAL_OVERRIDE;
- //the resulting values should be used for input to the transformation
- //received with 'getTransformationScaledLogicToScene'
+ //the resulting values provided by the following 3 methods should be used
+ //for input to the transformation received with
+ //'getTransformationScaledLogicToScene'
+
+ /** Given a value in the radius axis scale range, it returns the normalized
+ * value.
+ */
double transformToRadius( double fLogicValueOnRadiusAxis, bool bDoScaling=true ) const;
+
+ /** Given a value in the angle axis scale range (e.g. [0,1] for pie charts)
+ * this method returns the related angle in degree.
+ */
double transformToAngleDegree( double fLogicValueOnAngleAxis, bool bDoScaling=true ) const;
+
+ /** Given 2 values in the angle axis scale range (e.g. [0,1] for pie charts)
+ * this method returns the angle between the 2 values keeping into account
+ * the correct axis orientation; (for instance, this method is used for
+ * computing the angle width of a pie slice).
+ */
double getWidthAngleDegree( double& fStartLogicValueOnAngleAxis, double& fEndLogicValueOnAngleAxis ) const;
virtual ::com::sun::star::drawing::Position3D
@@ -172,6 +187,11 @@ public:
transformScaledLogicToScene( double fX, double fY, double fZ, bool bClip ) const SAL_OVERRIDE;
::com::sun::star::drawing::Position3D
transformAngleRadiusToScene( double fLogicValueOnAngleAxis, double fLogicValueOnRadiusAxis, double fLogicZ, bool bDoScaling=true ) const;
+
+ /** It returns the scene coordinates of the passed point: this point is
+ * described through a normalized cylindrical coordinate system.
+ * (For a pie chart the origin of the coordinate system is the pie center).
+ */
::com::sun::star::drawing::Position3D
transformUnitCircleToScene( double fUnitAngleDegree, double fUnitRadius, double fLogicZ, bool bDoScaling=true ) const;
@@ -182,9 +202,18 @@ public:
inline bool isMathematicalOrientationAngle() const;
inline bool isMathematicalOrientationRadius() const;
public:
+ //m_bSwapXAndY (inherited): by default the X axis (scale[0]) represents
+ //the angle axis and the Y axis (scale[1]) represents the radius axis;
+ //when this parameter is true, the opposite happens (this is the case for
+ //pie charts).
+
//Offset for radius axis in absolute logic scaled values (1.0 == 1 category)
+ //For a donut, it represents the non-normalized inner radius (see notes for
+ //transformToRadius)
double m_fRadiusOffset;
- //Offset for angle axis in real degree
+ //Offset for angle axis in real degree.
+ //For a pie it represents the angle offset at which the first slice have to
+ //start;
double m_fAngleDegreeOffset;
private:
diff --git a/chart2/source/view/inc/PolarLabelPositionHelper.hxx b/chart2/source/view/inc/PolarLabelPositionHelper.hxx
index 5b9b1d4487e8..cb7ae63ad887 100644
--- a/chart2/source/view/inc/PolarLabelPositionHelper.hxx
+++ b/chart2/source/view/inc/PolarLabelPositionHelper.hxx
@@ -48,6 +48,17 @@ public:
, double fLogicZ
, sal_Int32 nScreenValueOffsetInRadiusDirection=0 ) const;
+ /** Calculate the anchor point position for a text label.
+ * When the requested label placement is of `INSIDE` or `OUTSIDE` type the
+ * returned anchor point for the text label is the middle point of the
+ * outer arc for the given slice; when the requested label placement is of
+ * `CENTER` type the returned anchor point for the text label is the
+ * middle point of the line segment bisecting the slice.
+ * The text alignment is always centered when the requested label
+ * placement is of `CENTER` type else it is dependent on the value of the
+ * angle defined by the horizontal axis and the ray bisecting the slice.
+ *
+ */
::com::sun::star::awt::Point getLabelScreenPositionAndAlignmentForUnitCircleValues(
LabelAlignment& rAlignment, sal_Int32 nLabelPlacement /*see ::com::sun::star::chart::DataLabelPlacement*/
, double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree
diff --git a/chart2/source/view/inc/VSeriesPlotter.hxx b/chart2/source/view/inc/VSeriesPlotter.hxx
index 16218f7a4a9d..03bbc8083eb2 100644
--- a/chart2/source/view/inc/VSeriesPlotter.hxx
+++ b/chart2/source/view/inc/VSeriesPlotter.hxx
@@ -179,6 +179,9 @@ public:
void getMinimumAndMaximiumX( double& rfMinimum, double& rfMaximum ) const;
void getMinimumAndMaximiumYInContinuousXRange( double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const;
+
+ // Methods for handling legends and legend entries.
+
virtual std::vector< ViewLegendEntry > createLegendEntries(
const ::com::sun::star::awt::Size& rEntryKeyAspectRatio,
::com::sun::star::chart::ChartLegendExpansion eLegendExpansion,
@@ -225,6 +228,7 @@ public:
::std::vector< VDataSeries* > getAllSeries();
+ // This method creates a series plotter of the requested type; e.g. : return new PieChart ....
static VSeriesPlotter* createSeriesPlotter( const ::com::sun::star::uno::Reference<
::com::sun::star::chart2::XChartType >& xChartTypeModel
, sal_Int32 nDimensionCount
@@ -232,6 +236,8 @@ public:
sal_Int32 getPointCount() const;
+ // Methods for number formats and color schemes
+
void setNumberFormatsSupplier( const ::com::sun::star::uno::Reference<
::com::sun::star::util::XNumberFormatsSupplier > & xNumFmtSupplier );
void setAxesNumberFormats( const AxesNumberFormats& rAxesNumberFormats ) { m_aAxesNumberFormats = rAxesNumberFormats; };
@@ -270,6 +276,8 @@ protected:
, sal_Int32 nDimensionCount
, bool bCategoryXAxis=true );
+ // Methods for group shapes.
+
::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes >
getSeriesGroupShape( VDataSeries* pDataSeries
, const::com::sun::star:: uno::Reference<
@@ -287,6 +295,8 @@ protected:
, const::com::sun::star:: uno::Reference<
::com::sun::star::drawing::XShapes >& xTarget );
+ /// This method creates a 2D group shape for containing all text shapes
+ /// needed for this series; the group is added to the text target;
::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes >
getLabelsGroupShape( VDataSeries& rDataSeries
, const::com::sun::star:: uno::Reference<
@@ -297,6 +307,29 @@ protected:
, const::com::sun::star:: uno::Reference<
::com::sun::star::drawing::XShapes >& xTarget, bool bYError );
+ /** This method creates a text shape for a label related to a data point
+ * and append it to the root text shape group (xTarget).
+ *
+ * @param xTarget
+ * the main root text shape group.
+ * @param rDataSeries
+ * the data series, the data point belongs to.
+ * @param nPointIndex
+ * the index of the data point the label is related to.
+ * @param fValue
+ * the value of the data point.
+ * @param fSumValue
+ * the sum of all data point values in the data series.
+ * @param rScreenPosition2D
+ * the anchor point position for the label.
+ * @param eAlignment
+ * the required alignment of the label.
+ * @param offset
+ * an optional offset depending on the label alignment.
+ *
+ * @return
+ * a reference to the created text shape.
+ */
::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >
createDataLabel( const ::com::sun::star::uno::Reference<
::com::sun::star::drawing::XShapes >& xTarget
@@ -308,6 +341,8 @@ protected:
, LabelAlignment eAlignment=LABEL_ALIGN_CENTER
, sal_Int32 nOffset=0 );
+ /// This method returns a text string representation of the passed numeric
+ /// value by exploiting a NumberFormatterWrapper object.
OUString getLabelTextForValue( VDataSeries& rDataSeries
, sal_Int32 nPointIndex
, double fValue
diff --git a/chart2/source/view/main/PlottingPositionHelper.cxx b/chart2/source/view/main/PlottingPositionHelper.cxx
index d72b1dd88021..957572674e74 100644
--- a/chart2/source/view/main/PlottingPositionHelper.cxx
+++ b/chart2/source/view/main/PlottingPositionHelper.cxx
@@ -452,6 +452,10 @@ double PolarPlottingPositionHelper::getWidthAngleDegree( double& fStartLogicValu
return fWidthAngleDegree;
}
+//This method does a lot of computation for understanding which scale to
+//utilize and if reverse orientation should be used. Indeed, for a pie or donut,
+//the final result is as simple as multiplying by 360 and adding
+//`m_fAngleDegreeOffset`.
double PolarPlottingPositionHelper::transformToAngleDegree( double fLogicValueOnAngleAxis, bool bDoScaling ) const
{
double fRet=0.0;
@@ -503,6 +507,75 @@ double PolarPlottingPositionHelper::transformToAngleDegree( double fLogicValueOn
return fRet;
}
+/*
+ * Given a value in the radius axis scale range, it returns, in the simplest
+ * case (that is when `m_fRadiusOffset` is zero), the normalized value; when
+ * `m_fRadiusOffset` is not zero (e.g. as in the case of a donut), the interval
+ * used for normalization is extended by `m_fRadiusOffset`: if the axis
+ * orientation is not reversed the new interval becomes
+ * [scale.Minimum - m_fRadiusOffset, scale.Maximum] else it becomes
+ * [scale.Minimum, scale.Maximum + m_fRadiusOffset].
+ * Pay attention here! For the latter case, since the axis orientation is
+ * reversed, the normalization is reversed too. Indeed, we have
+ * `transformToRadius(scale.Maximum + m_fRadiusOffset) = 0` and
+ * `transformToRadius(scale.Minimum) = 1`.
+ *
+ * For a pie chart the radius axis scale range is initialized by the
+ * `getMinimum` and `getMaximum` methods of the `PieChart` object (see notes
+ * for `VCoordinateSystem::prepareAutomaticAxisScaling`).
+ * So we have scale.Minimum = 0.5 (always constant!) and
+ * scale.Maximum = 0.5 + number_of_rings + max_offset
+ * (see notes for `PieChart::getMaxOffset`).
+ * Hence we get the following general formulas for computing normalized inner
+ * and outer radius:
+ *
+ * 1- transformToRadius(inner_radius) =
+ * (number_of_rings - (ring_index + 1) + m_fRadiusOffset)
+ * / (number_of_rings + max_offset + m_fRadiusOffset)
+ *
+ * 2- transformToRadius(outer_radius) =
+ * (1 + number_of_rings - (ring_index + 1) + m_fRadiusOffset)
+ * / (number_of_rings + max_offset + m_fRadiusOffset).
+ *
+ * Here you have to take into account that values for inner and outer radius
+ * are swapped since the radius axis is reversed (See notes for
+ * `PiePositionHelper::getInnerAndOuterRadius`). So indeed inner_radius is
+ * the outer and outer_radius is the inner. Anyway still because of the reverse
+ * orientation, the normalization performed by `transformToRadius` is reversed
+ * too, as we have seen above. Hence `transformToRadius(inner_radius)` is
+ * really the normalized inner radius and `transformToRadius(outer_radius)` is
+ * really the normalized outer radius.
+ *
+ * Some basic examples where we apply the above formulas:
+ * 1- For a non-exploded pie chart we have:
+ * `transformToRadius(inner_radius) = 0`,
+ * `transformToRadius(outer_radius) = 1`.
+ * 2- For a non-exploded donut with a single ring we have:
+ * `transformToRadius(inner_radius) =
+ * m_fRadiusOffset/(1 + m_fRadiusOffset)`,
+ * `transformToRadius(outer_radius) =
+ * (1 + m_fRadiusOffset)/(1 + m_fRadiusOffset) = 1`.
+ * 3- For an exploded pie chart we have:
+ * `transformToRadius(inner_radius) = 0/(1 + max_offset) = 0`,
+ * `transformToRadius(outer_radius) = 1/(1 + max_offset)`.
+ *
+ * The third example needs some remark. Both the logical inner and outer
+ * radius passed to `transformToRadius` are offset by `max_offset`.
+ * However the returned normalized values do not contain any (normalized)
+ * offset term at all, otherwise the returned values would be
+ * `max_offset/(1 + max_offset)` and `1`. Hence, for exploded pie/donut,
+ * `transformToRadius` returns the normalized value of radii without any
+ * offset term. These values are smaller than in the non-exploded case by an
+ * amount equals to the value of the normalized maximum offset
+ * (`max_offset/(1 + max_offset)` in the example above). That is due to the
+ * fact that the normalization keeps into account the space needed for the
+ * offset. This is the correct behavior, in fact the offset for the current
+ * slice could be different from the maximum offset.
+ * These remarks should clarify why the `PieChart::createDataPoint` and
+ * `PieChart::createTextLabelShape` methods add the normalized offset (for the
+ * current slice) to the normalized radii in order to achieve the correct
+ * placement of slice and text shapes.
+ */
double PolarPlottingPositionHelper::transformToRadius( double fLogicValueOnRadiusAxis, bool bDoScaling ) const
{
double fNormalRadius = 0.0;