tdf#50934: Restructuring for of-pie charts

Change-Id: I9682c314efb888d57e94f82f084cc6d0825a4408
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160723
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
This commit is contained in:
Kurt Nordback 2023-09-01 16:13:15 -06:00 committed by Noel Grandin
parent 0f4002378d
commit d81c98623b
2 changed files with 184 additions and 138 deletions

View file

@ -790,13 +790,6 @@ void PieChart::createShapes()
///the angle axis scale range is [0, 1]. The max_offset parameter is used
///for exploded pie chart and its value is 0.5.
///the `explodeable` ring is the first one except when the radius axis
///orientation is reversed (always!?) and we are dealing with a donut: in
///such a case the `explodeable` ring is the last one.
std::vector< VDataSeriesGroup >::size_type nExplodeableSlot = 0;
if( m_aPosHelper.isMathematicalOrientationRadius() && m_bUseRings )
nExplodeableSlot = m_aZSlots.front().size()-1;
m_aLabelInfoList.clear();
m_fMaxOffset = std::numeric_limits<double>::quiet_NaN();
sal_Int32 n3DRelativeHeight = 100;
@ -815,8 +808,6 @@ void PieChart::createShapes()
///(m_bUseRings||fSlotX<0.5)
for( double fSlotX=0; aXSlotIter != aXSlotEnd && (m_bUseRings||fSlotX<0.5 ); ++aXSlotIter, fSlotX+=1.0 )
{
ShapeParam aParam;
std::vector< std::unique_ptr<VDataSeries> >* pSeriesList = &(aXSlotIter->m_aSeriesVector);
if(pSeriesList->empty())//there should be only one series in each x slot
continue;
@ -824,8 +815,6 @@ void PieChart::createShapes()
if(!pSeries)
continue;
bool bHasFillColorMapping = pSeries->hasPropertyMapping("FillColor");
/// The angle degree offset is set by the same property of the
/// data series.
/// Counter-clockwise offset from the 3 o'clock position.
@ -835,6 +824,8 @@ void PieChart::createShapes()
///the current data series
sal_Int32 nPointIndex=0;
sal_Int32 nPointCount=pSeries->getTotalPointCount();
ShapeParam aParam;
for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ )
{
double fY = pSeries->getYValue( nPointIndex );
@ -851,136 +842,177 @@ void PieChart::createShapes()
// Total sum of all Y values in this series is zero. Skip the whole series.
continue;
double fLogicYForNextPoint = 0.0;
///iterate through all points to create shapes
for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ )
{
double fLogicInnerRadius, fLogicOuterRadius;
///compute the maximum relative distance offset of the current slice
///from the pie center
///it is worth noting that after the first invocation the maximum
///offset value is cached, so it is evaluated only once per each
///call to `createShapes`
double fOffset = getMaxOffset();
///compute the outer and the inner radius for the current ring slice
bool bIsVisible = m_aPosHelper.getInnerAndOuterRadius( fSlotX+1.0, fLogicInnerRadius, fLogicOuterRadius, m_bUseRings, fOffset );
if( !bIsVisible )
continue;
aParam.mfDepth = getTransformedDepth() * (n3DRelativeHeight / 100.0);
rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries, xSeriesTarget);
///collect data point information (logic coordinates, style ):
double fLogicYValue = fabs(pSeries->getYValue( nPointIndex ));
if( std::isnan(fLogicYValue) )
continue;
if(fLogicYValue==0.0)//@todo: continue also if the resolution is too small
continue;
double fLogicYPos = fLogicYForNextPoint;
fLogicYForNextPoint += fLogicYValue;
uno::Reference< beans::XPropertySet > xPointProperties = pSeries->getPropertiesOfPoint( nPointIndex );
//iterate through all subsystems to create partial points
{
//logic values on angle axis:
double fLogicStartAngleValue = fLogicYPos / aParam.mfLogicYSum;
double fLogicEndAngleValue = (fLogicYPos+fLogicYValue) / aParam.mfLogicYSum;
///note that the explode percentage is set to the `Offset`
///property of the current data series entry only for slices
///belonging to the outer ring
aParam.mfExplodePercentage = 0.0;
bool bDoExplode = ( nExplodeableSlot == static_cast< std::vector< VDataSeriesGroup >::size_type >(fSlotX) );
if(bDoExplode) try
{
xPointProperties->getPropertyValue( "Offset") >>= aParam.mfExplodePercentage;
}
catch( const uno::Exception& )
{
TOOLS_WARN_EXCEPTION("chart2", "" );
}
///see notes for `PolarPlottingPositionHelper` methods
///transform to unit circle:
aParam.mfUnitCircleWidthAngleDegree = m_aPosHelper.getWidthAngleDegree( fLogicStartAngleValue, fLogicEndAngleValue );
aParam.mfUnitCircleStartAngleDegree = m_aPosHelper.transformToAngleDegree( fLogicStartAngleValue );
aParam.mfUnitCircleInnerRadius = m_aPosHelper.transformToRadius( fLogicInnerRadius );
aParam.mfUnitCircleOuterRadius = m_aPosHelper.transformToRadius( fLogicOuterRadius );
///create data point
aParam.mfLogicZ = -1.0; // For 3D pie chart label position
// Do concentric explosion if it's a donut chart with more than one series
const bool bConcentricExplosion = m_bUseRings && (m_aZSlots.front().size() > 1);
rtl::Reference<SvxShape> xPointShape =
createDataPoint(
xSeriesGroupShape_Shapes, xPointProperties, aParam, nPointCount,
bConcentricExplosion);
///point color:
if (!pSeries->hasPointOwnColor(nPointIndex) && m_xColorScheme.is())
{
xPointShape->setPropertyValue("FillColor",
uno::Any(m_xColorScheme->getColorByIndex( nPointIndex )));
}
if(bHasFillColorMapping)
{
double nPropVal = pSeries->getValueByProperty(nPointIndex, "FillColor");
if(!std::isnan(nPropVal))
{
xPointShape->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>( nPropVal)));
}
}
///create label
createTextLabelShape(xTextTarget, *pSeries, nPointIndex, aParam);
if(!bDoExplode)
{
ShapeFactory::setShapeName( xPointShape
, ObjectIdentifier::createPointCID( pSeries->getPointCID_Stub(), nPointIndex ) );
}
else try
{
///enable dragging of outer segments
double fAngle = aParam.mfUnitCircleStartAngleDegree + aParam.mfUnitCircleWidthAngleDegree/2.0;
double fMaxDeltaRadius = aParam.mfUnitCircleOuterRadius-aParam.mfUnitCircleInnerRadius;
drawing::Position3D aOrigin = m_aPosHelper.transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius, aParam.mfLogicZ );
drawing::Position3D aNewOrigin = m_aPosHelper.transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius + fMaxDeltaRadius, aParam.mfLogicZ );
sal_Int32 nOffsetPercent( static_cast<sal_Int32>(aParam.mfExplodePercentage * 100.0) );
awt::Point aMinimumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
aOrigin, m_xLogicTarget, m_nDimension ) );
awt::Point aMaximumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
aNewOrigin, m_xLogicTarget, m_nDimension ) );
//enable dragging of piesegments
OUString aPointCIDStub( ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT
, pSeries->getSeriesParticle()
, ObjectIdentifier::getPieSegmentDragMethodServiceName()
, ObjectIdentifier::createPieSegmentDragParameterString(
nOffsetPercent, aMinimumPosition, aMaximumPosition )
) );
ShapeFactory::setShapeName( xPointShape
, ObjectIdentifier::createPointCID( aPointCIDStub, nPointIndex ) );
}
catch( const uno::Exception& )
{
TOOLS_WARN_EXCEPTION("chart2", "" );
}
}//next series in x slot (next y slot)
}//next category
switch (m_eSubType) {
case PieChartSubType_NONE:
createOneRing(SubPieType::NONE, fSlotX, aParam, xSeriesTarget, xTextTarget, pSeries, n3DRelativeHeight);
break;
case PieChartSubType_BAR:
createOneRing(SubPieType::LEFT, fSlotX, aParam, xSeriesTarget, xTextTarget, pSeries, n3DRelativeHeight);
break;
case PieChartSubType_PIE:
createOneRing(SubPieType::LEFT, fSlotX, aParam, xSeriesTarget, xTextTarget, pSeries, n3DRelativeHeight);
break;
default:
assert(false); // this shouldn't happen
}
}//next x slot
}
void PieChart::createOneRing([[maybe_unused]]enum SubPieType eType,
double fSlotX,
ShapeParam& aParam,
const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget,
const rtl::Reference<SvxShapeGroup>& xTextTarget,
VDataSeries* pSeries,
sal_Int32 n3DRelativeHeight)
{
bool bHasFillColorMapping = pSeries->hasPropertyMapping("FillColor");
/// The angle degree offset is set by the same property of the
/// data series.
/// Counter-clockwise offset from the 3 o'clock position.
m_aPosHelper.m_fAngleDegreeOffset = pSeries->getStartingAngle();
///iterate through all points to get the sum of all entries of
///the current data series
sal_Int32 nPointCount=pSeries->getTotalPointCount();
///the `explodeable` ring is the first one except when the radius axis
///orientation is reversed (always!?) and we are dealing with a donut: in
///such a case the `explodeable` ring is the last one.
std::vector< VDataSeriesGroup >::size_type nExplodeableSlot = 0;
if( m_aPosHelper.isMathematicalOrientationRadius() && m_bUseRings )
nExplodeableSlot = m_aZSlots.front().size()-1;
double fLogicYForNextPoint = 0.0;
///iterate through all points to create shapes
for(sal_Int32 nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ )
{
double fLogicInnerRadius, fLogicOuterRadius;
///compute the maximum relative distance offset of the current slice
///from the pie center
///it is worth noting that after the first invocation the maximum
///offset value is cached, so it is evaluated only once per each
///call to `createShapes`
double fOffset = getMaxOffset();
///compute the outer and the inner radius for the current ring slice
bool bIsVisible = m_aPosHelper.getInnerAndOuterRadius( fSlotX+1.0, fLogicInnerRadius, fLogicOuterRadius, m_bUseRings, fOffset );
if( !bIsVisible )
continue;
aParam.mfDepth = getTransformedDepth() * (n3DRelativeHeight / 100.0);
rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries, xSeriesTarget);
///collect data point information (logic coordinates, style ):
double fLogicYValue = fabs(pSeries->getYValue( nPointIndex ));
if( std::isnan(fLogicYValue) )
continue;
if(fLogicYValue==0.0)//@todo: continue also if the resolution is too small
continue;
double fLogicYPos = fLogicYForNextPoint;
fLogicYForNextPoint += fLogicYValue;
uno::Reference< beans::XPropertySet > xPointProperties = pSeries->getPropertiesOfPoint( nPointIndex );
//iterate through all subsystems to create partial points
{
//logic values on angle axis:
double fLogicStartAngleValue = fLogicYPos / aParam.mfLogicYSum;
double fLogicEndAngleValue = (fLogicYPos+fLogicYValue) / aParam.mfLogicYSum;
///note that the explode percentage is set to the `Offset`
///property of the current data series entry only for slices
///belonging to the outer ring
aParam.mfExplodePercentage = 0.0;
bool bDoExplode = ( nExplodeableSlot == static_cast< std::vector< VDataSeriesGroup >::size_type >(fSlotX) );
if(bDoExplode) try
{
xPointProperties->getPropertyValue( "Offset") >>= aParam.mfExplodePercentage;
}
catch( const uno::Exception& )
{
TOOLS_WARN_EXCEPTION("chart2", "" );
}
///see notes for `PolarPlottingPositionHelper` methods
///transform to unit circle:
aParam.mfUnitCircleWidthAngleDegree = m_aPosHelper.getWidthAngleDegree( fLogicStartAngleValue, fLogicEndAngleValue );
aParam.mfUnitCircleStartAngleDegree = m_aPosHelper.transformToAngleDegree( fLogicStartAngleValue );
aParam.mfUnitCircleInnerRadius = m_aPosHelper.transformToRadius( fLogicInnerRadius );
aParam.mfUnitCircleOuterRadius = m_aPosHelper.transformToRadius( fLogicOuterRadius );
///create data point
aParam.mfLogicZ = -1.0; // For 3D pie chart label position
// Do concentric explosion if it's a donut chart with more than one series
const bool bConcentricExplosion = m_bUseRings && (m_aZSlots.front().size() > 1);
rtl::Reference<SvxShape> xPointShape =
createDataPoint(
xSeriesGroupShape_Shapes, xPointProperties, aParam, nPointCount,
bConcentricExplosion);
///point color:
if (!pSeries->hasPointOwnColor(nPointIndex) && m_xColorScheme.is())
{
xPointShape->setPropertyValue("FillColor",
uno::Any(m_xColorScheme->getColorByIndex( nPointIndex )));
}
if(bHasFillColorMapping)
{
double nPropVal = pSeries->getValueByProperty(nPointIndex, "FillColor");
if(!std::isnan(nPropVal))
{
xPointShape->setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>( nPropVal)));
}
}
///create label
createTextLabelShape(xTextTarget, *pSeries, nPointIndex, aParam);
if(!bDoExplode)
{
ShapeFactory::setShapeName( xPointShape
, ObjectIdentifier::createPointCID( pSeries->getPointCID_Stub(), nPointIndex ) );
}
else try
{
///enable dragging of outer segments
double fAngle = aParam.mfUnitCircleStartAngleDegree + aParam.mfUnitCircleWidthAngleDegree/2.0;
double fMaxDeltaRadius = aParam.mfUnitCircleOuterRadius-aParam.mfUnitCircleInnerRadius;
drawing::Position3D aOrigin = m_aPosHelper.transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius, aParam.mfLogicZ );
drawing::Position3D aNewOrigin = m_aPosHelper.transformUnitCircleToScene( fAngle, aParam.mfUnitCircleOuterRadius + fMaxDeltaRadius, aParam.mfLogicZ );
sal_Int32 nOffsetPercent( static_cast<sal_Int32>(aParam.mfExplodePercentage * 100.0) );
awt::Point aMinimumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
aOrigin, m_xLogicTarget, m_nDimension ) );
awt::Point aMaximumPosition( PlottingPositionHelper::transformSceneToScreenPosition(
aNewOrigin, m_xLogicTarget, m_nDimension ) );
//enable dragging of piesegments
OUString aPointCIDStub( ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT
, pSeries->getSeriesParticle()
, ObjectIdentifier::getPieSegmentDragMethodServiceName()
, ObjectIdentifier::createPieSegmentDragParameterString(
nOffsetPercent, aMinimumPosition, aMaximumPosition )
) );
ShapeFactory::setShapeName( xPointShape
, ObjectIdentifier::createPointCID( aPointCIDStub, nPointIndex ) );
}
catch( const uno::Exception& )
{
TOOLS_WARN_EXCEPTION("chart2", "" );
}
}//next series in x slot (next y slot)
}//next category
}
PieChart::PieLabelInfo::PieLabelInfo()
: fValue(0.0)
, bMovementAllowed(false), bMoved(false)

View file

@ -75,6 +75,12 @@ public:
virtual bool isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex ) override;
virtual bool isSeparateStackingForDifferentSigns( sal_Int32 nDimensionIndex ) override;
enum class SubPieType {
NONE,
LEFT,
RIGHT
};
private: //methods
rtl::Reference<SvxShape>
createDataPoint(
@ -122,6 +128,14 @@ struct PieLabelInfo;
bool performLabelBestFitInnerPlacement( ShapeParam& rShapeParam
, PieLabelInfo const & rPieLabelInfo );
void createOneRing([[maybe_unused]]enum SubPieType eType
, double fSlotX
, ShapeParam& aParam
, const rtl::Reference<SvxShapeGroupAnyD>& xSeriesTarget
, const rtl::Reference<SvxShapeGroup>& xTextTarget
, VDataSeries* pSeries
, sal_Int32 n3DRelativeHeight);
private: //member
PiePositionHelper m_aPosHelper;
bool m_bUseRings;