sc/source/ui/inc/output.hxx    |    1 
 sc/source/ui/view/gridwin4.cxx |    2 
 sc/source/ui/view/output.cxx   |  202 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 204 insertions(+), 1 deletion(-)

New commits:
commit f2d07a3b17430d21d4567f7a01525a702544ed8d
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Thu Feb 24 17:43:00 2022 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Sun Mar 13 15:34:16 2022 +0100

    sc: initial code to draw sparkline into a cell
    
    Change-Id: I82861f4210a24f57573f0ec96489e86ab168677b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131488
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/sc/source/ui/inc/output.hxx b/sc/source/ui/inc/output.hxx
index 61873156d264..d44f7052589b 100644
--- a/sc/source/ui/inc/output.hxx
+++ b/sc/source/ui/inc/output.hxx
@@ -382,6 +382,7 @@ public:
 
     void    DrawNoteMarks(vcl::RenderContext& rRenderContext);
     void    AddPDFNotes();
+    void    DrawSparklines(vcl::RenderContext& rRenderContext);
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx
index 3598d5794828..e1a2aa318df3 100644
--- a/sc/source/ui/view/gridwin4.cxx
+++ b/sc/source/ui/view/gridwin4.cxx
@@ -903,6 +903,8 @@ void ScGridWindow::DrawContent(OutputDevice &rDevice, const 
ScTableInfo& rTableI
     aOutputData.DrawShadow();
     aOutputData.DrawFrame(*pContentDev);
 
+    aOutputData.DrawSparklines(*pContentDev);
+
     // Show Note Mark
     if ( rOpts.GetOption( VOPT_NOTES ) )
         aOutputData.DrawNoteMarks(*pContentDev);
diff --git a/sc/source/ui/view/output.cxx b/sc/source/ui/view/output.cxx
index 09a1fc041eeb..d986460bca52 100644
--- a/sc/source/ui/view/output.cxx
+++ b/sc/source/ui/view/output.cxx
@@ -30,7 +30,6 @@
 #include <svx/framelinkarray.hxx>
 #include <drawinglayer/geometry/viewinformation2d.hxx>
 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
-#include <basegfx/matrix/b2dhommatrix.hxx>
 #include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
 #include <vcl/lineinfo.hxx>
 #include <vcl/gradient.hxx>
@@ -39,6 +38,10 @@
 #include <sal/log.hxx>
 #include <comphelper/lok.hxx>
 #include <o3tl/unit_conversion.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2drectangle.hxx>
 
 #include <output.hxx>
 #include <document.hxx>
@@ -60,6 +63,7 @@
 #include <detfunc.hxx>
 
 #include <colorscale.hxx>
+#include <Sparkline.hxx>
 
 #include <math.h>
 #include <memory>
@@ -2315,6 +2319,202 @@ void ScOutputData::DrawChangeTrack()
     }
 }
 
+namespace
+{
+
+/** Draw a line chart into the rectangle bounds */
+void drawLine(vcl::RenderContext& rRenderContext, tools::Rectangle const & 
rRectangle,
+                std::vector<double> const & rValues, double nMin, double nMax)
+{
+    basegfx::B2DPolygon aPolygon;
+    double numebrOfSteps = rValues.size() - 1;
+    double xStep = 0;
+    double nDelta = nMax - nMin;
+
+    for (double aValue : rValues)
+    {
+        double nP = (aValue - nMin) / nDelta;
+        double x = rRectangle.GetWidth() * (xStep / numebrOfSteps);
+        double y = rRectangle.GetHeight() - rRectangle.GetHeight() * nP;
+
+        aPolygon.append({ x, y } );
+        xStep++;
+    }
+
+    basegfx::B2DHomMatrix aMatrix;
+    aMatrix.translate(rRectangle.Left(), rRectangle.Top());
+    aPolygon.transform(aMatrix);
+
+    rRenderContext.DrawPolyLine(aPolygon);
+}
+
+/** Draw a column chart into the rectangle bounds */
+void drawColumn(vcl::RenderContext& rRenderContext, tools::Rectangle const & 
rRectangle,
+                std::vector<double> const & rValues, double nMin, double nMax)
+{
+    basegfx::B2DPolygon aPolygon;
+
+    double xStep = 0;
+    double numberOfSteps = rValues.size();
+    double nDelta = nMax - nMin;
+
+    double nColumnSize = rRectangle.GetWidth() / numberOfSteps;
+
+    double nZero = (0 - nMin) / nDelta;
+    double nZeroPosition;
+    if (nZero >= 0)
+        nZeroPosition = rRectangle.GetHeight() - rRectangle.GetHeight() * 
nZero;
+    else
+        nZeroPosition = rRectangle.GetHeight();
+
+    for (double aValue : rValues)
+    {
+        if (aValue != 0.0)
+        {
+            double nP = (aValue - nMin) / nDelta;
+            double x = rRectangle.GetWidth() * (xStep / numberOfSteps);
+            double y = rRectangle.GetHeight() - rRectangle.GetHeight() * nP;
+
+            basegfx::B2DRectangle aRectangle(x, y, x + nColumnSize, 
nZeroPosition);
+            aPolygon = basegfx::utils::createPolygonFromRect(aRectangle);
+
+            basegfx::B2DHomMatrix aMatrix;
+            aMatrix.translate(rRectangle.Left(), rRectangle.Top());
+            aPolygon.transform(aMatrix);
+            rRenderContext.DrawPolygon(aPolygon);
+        }
+        xStep++;
+    }
+}
+
+void drawSparkline(sc::Sparkline* pSparkline, vcl::RenderContext& 
rRenderContext, ScDocument* pDocument,
+                                 tools::Rectangle const & rRectangle)
+{
+    auto const & rRangeList = pSparkline->getInputRange();
+
+    if (rRangeList.empty())
+        return;
+
+    auto pSparklineGroup = pSparkline->getSparklineGroup();
+
+    rRenderContext.SetAntialiasing(AntialiasingFlags::Enable);
+
+    rRenderContext.SetLineColor(pSparklineGroup->m_aColorSeries);
+    rRenderContext.SetFillColor(pSparklineGroup->m_aColorSeries);
+
+    ScRange aRange = rRangeList[0];
+
+    std::vector<double> aValues;
+
+    double nMin = std::numeric_limits<double>::max();
+    double nMax = std::numeric_limits<double>::min();
+
+    if (aRange.aStart.Row() == aRange.aEnd.Row())
+    {
+        ScAddress aAddress = aRange.aStart;
+
+        while (aAddress.Col() <= aRange.aEnd.Col())
+        {
+            double fCellValue = pDocument->GetValue(aAddress);
+            aValues.push_back(fCellValue);
+            if (fCellValue < nMin)
+                nMin = fCellValue;
+            if (fCellValue > nMax)
+                nMax = fCellValue;
+            aAddress.IncCol();
+        }
+    }
+    else if (aRange.aStart.Col() == aRange.aEnd.Col())
+    {
+        ScAddress aAddress = aRange.aStart;
+
+        while (aAddress.Row() <= aRange.aEnd.Row())
+        {
+            double fCellValue = pDocument->GetValue(aAddress);
+            aValues.push_back(fCellValue);
+            if (fCellValue < nMin)
+                nMin = fCellValue;
+            if (fCellValue > nMax)
+                nMax = fCellValue;
+            aAddress.IncRow();
+        }
+    }
+
+    if (pSparklineGroup->m_sType == "column")
+    {
+        drawColumn(rRenderContext, rRectangle, aValues, nMin, nMax);
+    }
+    else if (pSparklineGroup->m_sType == "stacked")
+    {
+        for (auto & rValue : aValues)
+        {
+            if (rValue != 0.0)
+                rValue = rValue > 0.0 ? 1.0 : -1.0;
+        }
+        drawColumn(rRenderContext, rRectangle, aValues, -1, 1);
+    }
+    else if (pSparklineGroup->m_sType == "line")
+    {
+        drawLine(rRenderContext, rRectangle, aValues, nMin, nMax);
+    }
+}
+} // end anonymous namespace
+
+void ScOutputData::DrawSparklines(vcl::RenderContext& rRenderContext)
+{
+    tools::Long nInitPosX = nScrX;
+    if ( bLayoutRTL )
+        nInitPosX += nMirrorW - 1;              // always in pixels
+    tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+    tools::Long nPosY = nScrY;
+    for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+    {
+        RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+        if ( pThisRowInfo->bChanged )
+        {
+            tools::Long nPosX = nInitPosX;
+            for (SCCOL nX=nX1; nX<=nX2; nX++)
+            {
+                ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+                bool bIsMerged = false;
+
+                if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
+                {
+                    // find start of merged cell
+                    bIsMerged = true;
+                    SCROW nY = pRowInfo[nArrY].nRowNo;
+                    SCCOL nMergeX = nX;
+                    SCROW nMergeY = nY;
+                    mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
+                }
+
+                sc::Sparkline* pSparkline = nullptr;
+                ScAddress aCurrentAddress(nX, pRowInfo[nArrY].nRowNo, nTab);
+
+                if (!mpDoc->ColHidden(nX, nTab) && (pSparkline = 
mpDoc->GetSparkline(aCurrentAddress))
+                    && (bIsMerged || (!pInfo->bHOverlapped && 
!pInfo->bVOverlapped)))
+                {
+                    constexpr tools::Long constMarginX = 6;
+                    constexpr tools::Long constMarginY = 3;
+
+                    const tools::Long nWidth = 
pRowInfo[0].basicCellInfo(nX).nWidth;
+                    const tools::Long nHeight = pThisRowInfo->nHeight;
+
+                    Point aPoint(nPosX + constMarginX , nPosY + constMarginY);
+                    Size aSize(nWidth - 2 * constMarginX, nHeight - 2 * 
constMarginY);
+
+                    drawSparkline(pSparkline, rRenderContext, mpDoc, 
tools::Rectangle(aPoint, aSize));
+                }
+
+                nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+            }
+        }
+        nPosY += pThisRowInfo->nHeight;
+    }
+
+}
+
 //TODO: moggi Need to check if this can't be written simpler
 void ScOutputData::DrawNoteMarks(vcl::RenderContext& rRenderContext)
 {

Reply via email to