chart2/source/view/charttypes/PieChart.cxx | 92 ++++++++++++++++------------- chart2/source/view/charttypes/PieChart.hxx | 9 ++ 2 files changed, 62 insertions(+), 39 deletions(-)
New commits: commit 86dcdfd097b0fdd91d69ecb8be0e72a84112a514 Author: Kurt Nordback <kurt.nordb...@protonmail.com> AuthorDate: Wed May 15 17:27:22 2024 -0600 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Thu May 16 10:56:21 2024 +0200 tdf#159547 of-pie connector lines aren't quite geometrically correct Fixed code computing the left endpoints of of-pie connector lines, so that they're tangent to the circle when the composite slice is large enough (and they terminate at the tangent point). Change-Id: I4ff4d8e31f47259f0f48721b532dc245ede83003 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167712 Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> Tested-by: Jenkins diff --git a/chart2/source/view/charttypes/PieChart.cxx b/chart2/source/view/charttypes/PieChart.cxx index d99e3d944af3..fcb79ef1907a 100644 --- a/chart2/source/view/charttypes/PieChart.cxx +++ b/chart2/source/view/charttypes/PieChart.cxx @@ -807,6 +807,47 @@ bool PieChart::isSeparateStackingForDifferentSigns( sal_Int32 /* nDimensionIndex return false; } +// Determine left endpoints of connecting lines. These will terminate either +// at the corners of the composite wedge (if the wedge is small enough), or +// tangent to the left pie circle (if the wedge is larger). The endpoints +// are at the returned values (xl0, +/-yl0). +// static +void PieChart::leftConnEndpoints(double* xl0_p, double* yl0_p, + const PieDataSrcBase *pDataSrc, + const VDataSeries *pSeries, + const ShapeParam &aParam) +{ + const sal_Int32 nEnd = pDataSrc->getNPoints(pSeries, SubPieType::LEFT); + const double compFrac = pDataSrc->getData(pSeries, nEnd - 1, + SubPieType::LEFT) / aParam.mfLogicYSum; + + // Assuming temporarily that the left circle is at the origin, + // the tangent point (xp0, yp0) on the left circle satisfies + // (1) xp0 = (1-r) / t + // (2) xp0^2 + yp0^2 = 1 + // where the left-hand circle has radius 1, the right-hand circle + // has radius r, and the right-hand circle is centered at (t, 0). + const double r0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale; + const double rho = m_fRightScale / m_fLeftScale; + const double xp0 = (1 - rho) / (m_fRightShift - m_fLeftShift); + // Determine if the composite wedge is large enough that the + // connecting lines hit the tangent point, instead of the corners of + // the wedge + assert(abs(xp0) <= 1.0); + const double theta = acos(xp0); + + double xl0, yl0; + if (compFrac < theta / M_PI) { + xl0 = r0 * cos(compFrac * M_PI); + yl0 = r0 * sin(compFrac * M_PI); + } else { + xl0 = r0 * xp0; + yl0 = sqrt(r0 * r0 - xl0 * xl0); + } + *xl0_p = xl0; + *yl0_p = yl0; +} + void PieChart::createShapes() { ///a ZSlot is a vector< vector< VDataSeriesGroup > >. There is only one @@ -941,19 +982,9 @@ void PieChart::createShapes() // double xl0, xl1, yl0, yl1, x0, y0, x1, y1, y2, y3; - // Get coordinates of "corners" of left composite wedge - sal_Int32 nEnd = pDataSrc->getNPoints(pSeries, SubPieType::LEFT); - double compFrac = pDataSrc->getData(pSeries, nEnd - 1, - SubPieType::LEFT) / aParam.mfLogicYSum; - if (compFrac < 0.5) { - xl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale * - cos(compFrac * M_PI) + m_fLeftShift; - yl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale * - sin(compFrac * M_PI); - } else { - xl0 = m_fLeftShift; - yl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale; - } + leftConnEndpoints(&xl0, &yl0, pDataSrc, pSeries, aParam); + + xl0 += m_fLeftShift; // Coordinates of bar top left corner xl1 = m_fBarLeft; @@ -1000,39 +1031,22 @@ void PieChart::createShapes() // double xl0, xl1, yl0, yl1, x0, y0, x1, y1, y2, y3; - // Get coordinates of "corners" of left composite wedge - sal_Int32 nEnd = pDataSrc->getNPoints(pSeries, SubPieType::LEFT); - double compFrac = pDataSrc->getData(pSeries, nEnd - 1, - SubPieType::LEFT) / aParam.mfLogicYSum; - // The following isn't quite right. The tangent points on the left - // pie are only at pi/2 and -pi/2 for composite wedges over 1/2 the - // total if left and right pies are the same diameter. And the - // threshold of 1/2 isn't quite right either. So there - // really should be a more sophisticated approach here. TODO - if (compFrac < 0.5) { - // Translated, per below - xl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale * - cos(compFrac * M_PI) + m_fLeftShift - m_fRightShift; - yl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale * - sin(compFrac * M_PI); - } else { - // Translated, per below - xl0 = m_fLeftShift - m_fRightShift; - yl0 = aParam.mfUnitCircleOuterRadius * m_fLeftScale; - } + leftConnEndpoints(&xl0, &yl0, pDataSrc, pSeries, aParam); + + // Translated, per below + xl0 += m_fLeftShift - m_fRightShift; // Compute tangent point on the right-hand circle of the line // through (xl0, yl0). If we translate things so the right-hand // circle is centered on the origin, then this point (x,y) - // satisfies these two equations, where r is the radius of the + // satisfies these two equations, where r1 is the radius of the // right-hand circle: - // (1) x^2 + y^2 = r^2 + // (1) x^2 + y^2 = r1^2 // (2) (y - yl0) / (x - xl0) = -x / y - const double r = aParam.mfUnitCircleOuterRadius * m_fRightScale; - - xl1 = (r*r * xl0 + yl0 * r * sqrt(xl0*xl0 + yl0*yl0 - r*r)) / + const double r1 = aParam.mfUnitCircleOuterRadius * m_fRightScale; + xl1 = (r1*r1 * xl0 + yl0 * r1 * sqrt(xl0*xl0 + yl0*yl0 - r1*r1)) / (xl0*xl0 + yl0*yl0); - yl1 = sqrt(r*r - xl1*xl1); + yl1 = sqrt(r1*r1 - xl1*xl1); // Now translate back to the coordinates we use xl0 += m_fRightShift; diff --git a/chart2/source/view/charttypes/PieChart.hxx b/chart2/source/view/charttypes/PieChart.hxx index ccbe9cb94d94..a7b158435d17 100644 --- a/chart2/source/view/charttypes/PieChart.hxx +++ b/chart2/source/view/charttypes/PieChart.hxx @@ -225,6 +225,15 @@ struct PieLabelInfo; const PieDataSrcBase *pDataSrc, sal_Int32 n3DRelativeHeight); + // Determine left endpoints of connecting lines. These will terminate either + // at the corners of the composite wedge (if the wedge is small enough), or + // tangent to the left pie circle (if the wedge is larger). The endpoints + // are at the returned values (xl0, +/-yl0). + static void leftConnEndpoints(double* xl0_p, double* yl0_p, + const PieDataSrcBase *pDataSrc, + const VDataSeries *pSeries, + const ShapeParam &aParam); + private: //member // Constants for of-pie charts. Some of these will want to become // user-selectable values. TODO