Consolidate more shadow generation code.

Moves the spot shadow code into the base class, and has both shadow
types use it to generate their geometry.

Bug: skia:7971
Change-Id: I866aa8a8a796a5f4bbf4698e1df5148c547ee3c2
Reviewed-on: https://skia-review.googlesource.com/152662
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index 6c55ddd..7956f0e 100644
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -43,9 +43,21 @@
     int vertexCount() const { return fPositions.count(); }
     int indexCount() const { return fIndices.count(); }
 
+    // initialization methods
     bool accumulateCentroid(const SkPoint& c, const SkPoint& n);
     bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2);
     void finishPathPolygon();
+
+    // convex shadow methods
+    bool computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip);
+    void computeClipVectorsAndTestCentroid();
+    bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
+    void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, bool lastEdge, bool doClip);
+    bool addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex);
+    int getClosestUmbraPoint(const SkPoint& point);
+
+    // concave shadow methods
+    bool computeConcaveShadow(SkScalar inset, SkScalar outset);
     void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
                             SkTDArray<int>* umbraIndices,
                             const SkTDArray<SkPoint>& penumbraPolygon,
@@ -61,7 +73,7 @@
 
     void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
 
-    bool addArc(const SkVector& nextNormal, bool finishArc);
+    bool addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc);
 
     void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
     void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
@@ -80,6 +92,10 @@
     SkTDArray<uint16_t> fIndices;
 
     SkTDArray<SkPoint>  fPathPolygon;
+    SkTDArray<SkPoint>  fUmbraPolygon;
+    SkTDArray<SkPoint>  fClipPolygon;
+    SkTDArray<SkVector> fClipVectors;
+
     SkPoint             fCentroid;
     SkScalar            fArea;
     SkScalar            fLastArea;
@@ -92,13 +108,17 @@
     bool                fSucceeded;
     bool                fTransparent;
     bool                fIsConvex;
+    bool                fValidUmbra;
 
     SkColor             fUmbraColor;
     SkColor             fPenumbraColor;
 
-    SkScalar            fRadius;
     SkScalar            fDirection;
     int                 fPrevUmbraIndex;
+    int                 fCurrUmbraIndex;
+    int                 fCurrClipIndex;
+    bool                fPrevUmbraOutside;
+    bool                fFirstUmbraOutside;
     SkVector            fPrevOutset;
     SkPoint             fPrevPoint;
 };
@@ -141,8 +161,13 @@
         , fSucceeded(false)
         , fTransparent(transparent)
         , fIsConvex(true)
+        , fValidUmbra(true)
         , fDirection(1)
-        , fPrevUmbraIndex(-1) {
+        , fPrevUmbraIndex(-1)
+        , fCurrUmbraIndex(0)
+        , fCurrClipIndex(0)
+        , fPrevUmbraOutside(false)
+        , fFirstUmbraOutside(false) {
     // child classes will set reserve for positions, colors and indices
 }
 
@@ -214,6 +239,306 @@
     fDirection = fArea > 0 ? -1 : 1;
 }
 
+bool SkBaseShadowTessellator::computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip) {
+    if (doClip) {
+        this->computeClipVectorsAndTestCentroid();
+    }
+
+    // generate inner ring
+    if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
+                              &fUmbraPolygon)) {
+        // not ideal, but in this case we'll inset using the centroid
+        fValidUmbra = false;
+    }
+
+    // walk around the path polygon, generate outer ring and connect to inner ring
+    if (fTransparent) {
+        fPositions.push_back(fCentroid);
+        fColors.push_back(fUmbraColor);
+    }
+    fCurrUmbraIndex = 0;
+
+    // initial setup
+    // add first quad
+    int polyCount = fPathPolygon.count();
+    if (!compute_normal(fPathPolygon[polyCount - 1], fPathPolygon[0], fDirection, &fFirstOutset)) {
+        // polygon should be sanitized by this point, so this is unrecoverable
+        return false;
+    }
+
+    fFirstOutset *= outset;
+    fFirstPoint = fPathPolygon[polyCount - 1];
+    fFirstVertexIndex = fPositions.count();
+    fPrevOutset = fFirstOutset;
+    fPrevPoint = fFirstPoint;
+    fPrevUmbraIndex = -1;
+
+    this->addInnerPoint(fFirstPoint, &fPrevUmbraIndex);
+
+    if (!fTransparent && doClip) {
+        SkPoint clipPoint;
+        bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
+                                              fCentroid, &clipPoint);
+        if (isOutside) {
+            fPositions.push_back(clipPoint);
+            fColors.push_back(fUmbraColor);
+        }
+        fPrevUmbraOutside = isOutside;
+        fFirstUmbraOutside = isOutside;
+    }
+
+    SkPoint newPoint = fFirstPoint + fFirstOutset;
+    fPositions.push_back(newPoint);
+    fColors.push_back(fPenumbraColor);
+    this->addEdge(fPathPolygon[0], fFirstOutset, false, doClip);
+
+    for (int i = 1; i < polyCount; ++i) {
+        SkVector normal;
+        if (!compute_normal(fPrevPoint, fPathPolygon[i], fDirection, &normal)) {
+            return false;
+        }
+        normal *= outset;
+        this->addArc(normal, outset, true);
+        this->addEdge(fPathPolygon[i], normal, i == polyCount - 1, doClip);
+    }
+    SkASSERT(this->indexCount());
+
+    // final fan
+    SkASSERT(fPositions.count() >= 3);
+    if (this->addArc(fFirstOutset, outset, false)) {
+        if (fFirstUmbraOutside) {
+            this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
+                                 fFirstVertexIndex + 2);
+        } else {
+            this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
+                                 fFirstVertexIndex + 1);
+        }
+    } else {
+        // no arc added, fix up by setting first penumbra point position to last one
+        if (fFirstUmbraOutside) {
+            fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
+        } else {
+            fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
+        }
+    }
+
+    return true;
+}
+
+void SkBaseShadowTessellator::computeClipVectorsAndTestCentroid() {
+    SkASSERT(fClipPolygon.count() >= 3);
+    fCurrClipIndex = fClipPolygon.count() - 1;
+
+    // init clip vectors
+    SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
+    SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
+    fClipVectors.push_back(v0);
+
+    // init centroid check
+    bool hiddenCentroid = true;
+    v1 = fCentroid - fClipPolygon[0];
+    SkScalar initCross = v0.cross(v1);
+
+    for (int p = 1; p < fClipPolygon.count(); ++p) {
+        // add to clip vectors
+        v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
+        fClipVectors.push_back(v0);
+        // Determine if transformed centroid is inside clipPolygon.
+        v1 = fCentroid - fClipPolygon[p];
+        if (initCross*v0.cross(v1) <= 0) {
+            hiddenCentroid = false;
+        }
+    }
+    SkASSERT(fClipVectors.count() == fClipPolygon.count());
+
+    fTransparent = fTransparent || !hiddenCentroid;
+}
+
+void SkBaseShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
+                                      bool lastEdge, bool doClip) {
+    // add next umbra point
+    int currUmbraIndex;
+    bool duplicate;
+    if (lastEdge) {
+        duplicate = false;
+        currUmbraIndex = fFirstVertexIndex;
+        fPrevPoint = nextPoint;
+    } else {
+        duplicate = this->addInnerPoint(nextPoint, &currUmbraIndex);
+    }
+    int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
+        ? fPositions.count() - 1
+        : fPositions.count() - 2;
+    if (!duplicate) {
+        // add to center fan if transparent or centroid showing
+        if (fTransparent) {
+            this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
+            // otherwise add to clip ring
+        } else if (doClip) {
+            SkPoint clipPoint;
+            bool isOutside = lastEdge ? fFirstUmbraOutside
+                : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
+                                       &clipPoint);
+            if (isOutside) {
+                if (!lastEdge) {
+                    fPositions.push_back(clipPoint);
+                    fColors.push_back(fUmbraColor);
+                }
+                this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
+                if (fPrevUmbraOutside) {
+                    // fill out quad
+                    this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
+                                         fPrevUmbraIndex + 1);
+                }
+            } else if (fPrevUmbraOutside) {
+                // add tri
+                this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
+            }
+
+            fPrevUmbraOutside = isOutside;
+        }
+    }
+
+    // add next penumbra point and quad
+    SkPoint newPoint = nextPoint + nextNormal;
+    fPositions.push_back(newPoint);
+    fColors.push_back(fPenumbraColor);
+
+    if (!duplicate) {
+        this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
+    }
+    this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
+
+    fPrevUmbraIndex = currUmbraIndex;
+    fPrevOutset = nextNormal;
+}
+
+bool SkBaseShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
+                                             SkPoint* clipPoint) {
+    SkVector segmentVector = centroid - umbraPoint;
+
+    int startClipPoint = fCurrClipIndex;
+    do {
+        SkVector dp = umbraPoint - fClipPolygon[fCurrClipIndex];
+        SkScalar denom = fClipVectors[fCurrClipIndex].cross(segmentVector);
+        SkScalar t_num = dp.cross(segmentVector);
+        // if line segments are nearly parallel
+        if (SkScalarNearlyZero(denom)) {
+            // and collinear
+            if (SkScalarNearlyZero(t_num)) {
+                return false;
+            }
+            // otherwise are separate, will try the next poly segment
+            // else if crossing lies within poly segment
+        } else if (t_num >= 0 && t_num <= denom) {
+            SkScalar s_num = dp.cross(fClipVectors[fCurrClipIndex]);
+            // if umbra point is inside the clip polygon
+            if (s_num >= 0 && s_num <= denom) {
+                segmentVector *= s_num / denom;
+                *clipPoint = umbraPoint + segmentVector;
+                return true;
+            }
+        }
+        fCurrClipIndex = (fCurrClipIndex + 1) % fClipPolygon.count();
+    } while (fCurrClipIndex != startClipPoint);
+
+    return false;
+}
+
+bool SkBaseShadowTessellator::addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex) {
+    SkPoint umbraPoint;
+    if (!fValidUmbra) {
+        SkVector v = fCentroid - pathPoint;
+        v *= 0.95f;
+        umbraPoint = pathPoint + v;
+    } else {
+        umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
+    }
+
+    fPrevPoint = pathPoint;
+
+    // merge "close" points
+    if (fPrevUmbraIndex == -1 ||
+        !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
+        // if we've wrapped around, don't add a new point
+        if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
+            *currUmbraIndex = fFirstVertexIndex;
+        } else {
+            *currUmbraIndex = fPositions.count();
+            fPositions.push_back(umbraPoint);
+            fColors.push_back(fUmbraColor);
+        }
+        return false;
+    } else {
+        *currUmbraIndex = fPrevUmbraIndex;
+        return true;
+    }
+}
+
+int SkBaseShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
+    SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraIndex]);
+    int index = fCurrUmbraIndex;
+    int dir = 1;
+    int next = (index + dir) % fUmbraPolygon.count();
+
+    // init travel direction
+    SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
+    if (distance < minDistance) {
+        index = next;
+        minDistance = distance;
+    } else {
+        dir = fUmbraPolygon.count() - 1;
+    }
+
+    // iterate until we find a point that increases the distance
+    next = (index + dir) % fUmbraPolygon.count();
+    distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
+    while (distance < minDistance) {
+        index = next;
+        minDistance = distance;
+        next = (index + dir) % fUmbraPolygon.count();
+        distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
+    }
+
+    fCurrUmbraIndex = index;
+    return index;
+}
+
+bool SkBaseShadowTessellator::computeConcaveShadow(SkScalar inset, SkScalar outset) {
+    if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
+        return false;
+    }
+
+    // generate inner ring
+    SkTDArray<int> umbraIndices;
+    umbraIndices.setReserve(fPathPolygon.count());
+    if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
+                               &fUmbraPolygon, &umbraIndices)) {
+        // TODO: figure out how to handle this case
+        return false;
+    }
+
+    // generate outer ring
+    SkTDArray<SkPoint> penumbraPolygon;
+    SkTDArray<int> penumbraIndices;
+    penumbraPolygon.setReserve(fUmbraPolygon.count());
+    penumbraIndices.setReserve(fUmbraPolygon.count());
+    if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -outset,
+                               &penumbraPolygon, &penumbraIndices)) {
+        // TODO: figure out how to handle this case
+        return false;
+    }
+
+    if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
+        return false;
+    }
+
+    // attach the rings together
+    this->stitchConcaveRings(fUmbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
+
+    return true;
+}
+
 void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
                                                  SkTDArray<int>* umbraIndices,
                                                  const SkTDArray<SkPoint>& penumbraPolygon,
@@ -257,11 +582,11 @@
         }
     }
 
-    *fPositions.push() = penumbraPolygon[currPenumbra];
-    *fColors.push() = fPenumbraColor;
+    fPositions.push_back(penumbraPolygon[currPenumbra]);
+    fColors.push_back(fPenumbraColor);
     int prevPenumbraIndex = 0;
-    *fPositions.push() = umbraPolygon[currUmbra];
-    *fColors.push() = fUmbraColor;
+    fPositions.push_back(umbraPolygon[currUmbra]);
+    fColors.push_back(fUmbraColor);
     fPrevUmbraIndex = 1;
     indexMap[currUmbra] = 1;
 
@@ -272,12 +597,12 @@
 
         if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
             // advance both one step
-            *fPositions.push() = penumbraPolygon[nextPenumbra];
-            *fColors.push() = fPenumbraColor;
+            fPositions.push_back(penumbraPolygon[nextPenumbra]);
+            fColors.push_back(fPenumbraColor);
             int currPenumbraIndex = fPositions.count() - 1;
 
-            *fPositions.push() = umbraPolygon[nextUmbra];
-            *fColors.push() = fUmbraColor;
+            fPositions.push_back(umbraPolygon[nextUmbra]);
+            fColors.push_back(fUmbraColor);
             int currUmbraIndex = fPositions.count() - 1;
             indexMap[nextUmbra] = currUmbraIndex;
 
@@ -298,8 +623,8 @@
         while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
                (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
             // fill out penumbra arc
-            *fPositions.push() = penumbraPolygon[nextPenumbra];
-            *fColors.push() = fPenumbraColor;
+            fPositions.push_back(penumbraPolygon[nextPenumbra]);
+            fColors.push_back(fPenumbraColor);
             int currPenumbraIndex = fPositions.count() - 1;
 
             this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
@@ -314,8 +639,8 @@
         while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
                (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
             // fill out umbra arc
-            *fPositions.push() = umbraPolygon[nextUmbra];
-            *fColors.push() = fUmbraColor;
+            fPositions.push_back(umbraPolygon[nextUmbra]);
+            fColors.push_back(fUmbraColor);
             int currUmbraIndex = fPositions.count() - 1;
             indexMap[nextUmbra] = currUmbraIndex;
 
@@ -329,12 +654,12 @@
         }
     }
     // finish up by advancing both one step
-    *fPositions.push() = penumbraPolygon[nextPenumbra];
-    *fColors.push() = fPenumbraColor;
+    fPositions.push_back(penumbraPolygon[nextPenumbra]);
+    fColors.push_back(fPenumbraColor);
     int currPenumbraIndex = fPositions.count() - 1;
 
-    *fPositions.push() = umbraPolygon[nextUmbra];
-    *fColors.push() = fUmbraColor;
+    fPositions.push_back(umbraPolygon[nextUmbra]);
+    fColors.push_back(fUmbraColor);
     int currUmbraIndex = fPositions.count() - 1;
     indexMap[nextUmbra] = currUmbraIndex;
 
@@ -381,7 +706,7 @@
         }
     }
 
-    *fPathPolygon.push() = pSanitized;
+    fPathPolygon.push_back(pSanitized);
 }
 
 void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
@@ -461,11 +786,11 @@
     }
 }
 
-bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
+bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc) {
     // fill in fan from previous quad
     SkScalar rotSin, rotCos;
     int numSteps;
-    if (!SkComputeRadialSteps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps)) {
+    if (!SkComputeRadialSteps(fPrevOutset, nextNormal, offset, &rotSin, &rotCos, &numSteps)) {
         // recover as best we can
         numSteps = 0;
     }
@@ -474,15 +799,15 @@
         SkVector currNormal;
         currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
         currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
-        *fPositions.push() = fPrevPoint + currNormal;
-        *fColors.push() = fPenumbraColor;
+        fPositions.push_back(fPrevPoint + currNormal);
+        fColors.push_back(fPenumbraColor);
         this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
 
         prevNormal = currNormal;
     }
     if (finishArc && numSteps) {
-        *fPositions.push() = fPrevPoint + nextNormal;
-        *fColors.push() = fPenumbraColor;
+        fPositions.push_back(fPrevPoint + nextNormal);
+        fColors.push_back(fPenumbraColor);
         this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
     }
     fPrevOutset = nextNormal;
@@ -520,13 +845,8 @@
 
 private:
     bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
-    bool computeConvexShadow();
-    bool computeConcaveShadow();
 
-    void handlePolyPoint(const SkPoint& p, bool finalPoint);
-    void addEdge(const SkPoint& nextPoint, const SkVector& nextNormal, bool finalEdge);
-
-    static constexpr auto kInsetFactor = -0.5f;
+    static constexpr auto kInsetFactor = 0.5f;
 
     typedef SkBaseShadowTessellator INHERITED;
 };
@@ -545,7 +865,7 @@
     // the final alpha.
     fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
     fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
-    fRadius = SkDrawShadowMetrics::AmbientBlurRadius(baseZ);
+    SkScalar outset = SkDrawShadowMetrics::AmbientBlurRadius(baseZ);
 
     if (!this->computePathPolygon(path, ctm)) {
         return;
@@ -565,9 +885,9 @@
     fIndices.setReserve(12 * path.countPoints());
 
     if (fIsConvex) {
-        fSucceeded = this->computeConvexShadow();
+        fSucceeded = this->computeConvexShadow(kInsetFactor, outset, false);
     } else {
-        fSucceeded = this->computeConcaveShadow();
+        fSucceeded = this->computeConcaveShadow(kInsetFactor, outset);
     }
 }
 
@@ -615,164 +935,6 @@
     return true;
 }
 
-bool SkAmbientShadowTessellator::computeConvexShadow() {
-    int polyCount = fPathPolygon.count();
-    if (polyCount < 3) {
-        return false;
-    }
-
-    // Add center point for fan if needed
-    if (fTransparent) {
-        *fPositions.push() = fCentroid;
-        *fColors.push() = fUmbraColor;
-    }
-
-    // Initialize
-    SkVector normal;
-    if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &normal)) {
-        // the polygon should be sanitized, so any issues at this point are unrecoverable
-        return false;
-    }
-    fFirstPoint = fPathPolygon[polyCount - 1];
-    fFirstVertexIndex = fPositions.count();
-    fFirstOutset = normal;
-    fFirstOutset *= fRadius;
-
-    fPrevOutset = fFirstOutset;
-    fPrevPoint = fFirstPoint;
-    fPrevUmbraIndex = fFirstVertexIndex;
-
-    // Add the first quad
-    *fPositions.push() = fFirstPoint;
-    *fColors.push() = fUmbraColor;
-    *fPositions.push() = fFirstPoint + fFirstOutset;
-    *fColors.push() = fPenumbraColor;
-
-    this->addEdge(fPathPolygon[0], normal, false);
-
-    // Process the remaining points
-    for (int i = 1; i < fPathPolygon.count(); ++i) {
-        this->handlePolyPoint(fPathPolygon[i], i == fPathPolygon.count()-1);
-    }
-    SkASSERT(this->indexCount());
-
-    // Final fan
-    SkASSERT(fPositions.count() >= 3);
-    if (this->addArc(fFirstOutset, false)) {
-        this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, fFirstVertexIndex + 1);
-    } else {
-        // arc is too small, set the first penumbra point to be the same position
-        // as the last one
-        fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
-    }
-
-    return true;
-}
-
-bool SkAmbientShadowTessellator::computeConcaveShadow() {
-    if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
-        return false;
-    }
-
-    // generate inner ring
-    SkTDArray<SkPoint> umbraPolygon;
-    SkTDArray<int> umbraIndices;
-    umbraIndices.setReserve(fPathPolygon.count());
-    if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), 0.5f,
-                               &umbraPolygon, &umbraIndices)) {
-        // TODO: figure out how to handle this case
-        return false;
-    }
-
-    // generate outer ring
-    SkTDArray<SkPoint> penumbraPolygon;
-    SkTDArray<int> penumbraIndices;
-    penumbraPolygon.setReserve(umbraPolygon.count());
-    penumbraIndices.setReserve(umbraPolygon.count());
-
-    if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -fRadius,
-                               &penumbraPolygon, &penumbraIndices)) {
-        // TODO: figure out how to handle this case
-        return false;
-    }
-
-    if (!umbraPolygon.count() || !penumbraPolygon.count()) {
-        return false;
-    }
-
-    // attach the rings together
-    this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
-
-    return true;
-}
-
-void SkAmbientShadowTessellator::handlePolyPoint(const SkPoint& p, bool finalPoint)  {
-    SkVector normal;
-    if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
-        SkVector scaledNormal = normal;
-        scaledNormal *= fRadius;
-        this->addArc(scaledNormal, true);
-        this->addEdge(p, normal, finalPoint);
-    }
-}
-
-void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
-                                         bool finalEdge) {
-    // We compute the inset in two stages: first we inset by half the current normal,
-    // then on the next addEdge() we add half of the next normal to get an average of the two
-    SkVector insetNormal = nextNormal;
-    insetNormal *= 0.5f*kInsetFactor;
-
-    // Adding the other half of the average for the previous edge
-    fPositions[fPrevUmbraIndex] += insetNormal;
-
-    SkPoint umbraPoint;
-    if (finalEdge) {
-        // Again, adding the other half of the average for the previous edge
-        fPositions[fFirstVertexIndex] += insetNormal;
-        umbraPoint = fPositions[fFirstVertexIndex];
-    } else {
-        umbraPoint = nextPoint + insetNormal;
-    }
-    SkVector outsetNormal = nextNormal;
-    outsetNormal *= fRadius;
-    SkPoint penumbraPoint = nextPoint + outsetNormal;
-
-    // add next quad
-    int prevPenumbraIndex;
-    int currUmbraIndex;
-    if (finalEdge) {
-        prevPenumbraIndex = fPositions.count() - 1;
-        currUmbraIndex = fFirstVertexIndex;
-    } else {
-        prevPenumbraIndex = fPositions.count() - 1;
-        *fPositions.push() = umbraPoint;
-        *fColors.push() = fUmbraColor;
-        currUmbraIndex = fPositions.count() - 1;
-    }
-
-    *fPositions.push() = penumbraPoint;
-    *fColors.push() = fPenumbraColor;
-
-    // set triangularization to get best interpolation of color
-    if (fColors[fPrevUmbraIndex] > fUmbraColor) {
-        this->appendQuad(fPrevUmbraIndex, prevPenumbraIndex,
-                         currUmbraIndex, fPositions.count() - 1);
-    } else {
-        this->appendQuad(currUmbraIndex, fPositions.count() - 1,
-                         fPrevUmbraIndex, prevPenumbraIndex);
-    }
-
-    // if transparent, add to center fan
-    if (fTransparent) {
-        this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
-    }
-
-    fPrevUmbraIndex = currUmbraIndex;
-    fPrevPoint = nextPoint;
-    fPrevOutset = outsetNormal;
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class SkSpotShadowTessellator : public SkBaseShadowTessellator {
@@ -784,39 +946,8 @@
 private:
     bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
                                     const SkMatrix& shadowTransform);
-    void computeClipVectorsAndTestCentroid();
-    bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
-    int getClosestUmbraPoint(const SkPoint& point);
-
-    bool computeConvexShadow(SkScalar radius);
-    bool computeConcaveShadow(SkScalar radius);
-
-    bool handlePolyPoint(const SkPoint& p, bool lastPoint);
-
-    void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
-    bool addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex);
-    void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, bool lastEdge);
     void addToClip(const SkVector& nextPoint);
 
-    SkScalar offset(SkScalar z) {
-        float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
-        return fLightRadius*zRatio;
-    }
-
-    SkScalar            fLightZ;
-    SkScalar            fLightRadius;
-    SkScalar            fOffsetAdjust;
-
-    SkTDArray<SkPoint>  fClipPolygon;
-    SkTDArray<SkVector> fClipVectors;
-
-    SkTDArray<SkPoint>  fUmbraPolygon;
-    int                 fCurrClipPoint;
-    int                 fCurrUmbraPoint;
-    bool                fPrevUmbraOutside;
-    bool                fFirstUmbraOutside;
-    bool                fValidUmbra;
-
     typedef SkBaseShadowTessellator INHERITED;
 };
 
@@ -824,14 +955,7 @@
                                                  const SkPoint3& zPlaneParams,
                                                  const SkPoint3& lightPos, SkScalar lightRadius,
                                                  bool transparent)
-    : INHERITED(zPlaneParams, transparent)
-    , fLightZ(lightPos.fZ)
-    , fLightRadius(lightRadius)
-    , fOffsetAdjust(0)
-    , fCurrClipPoint(0)
-    , fPrevUmbraOutside(false)
-    , fFirstUmbraOutside(false)
-    , fValidUmbra(true) {
+    : INHERITED(zPlaneParams, transparent) {
 
     const SkRect& pathBounds = path.getBounds();
 
@@ -841,13 +965,13 @@
     fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
 
     // Compute the blur radius, scale and translation for the spot shadow.
-    SkScalar radius;
+    SkScalar outset;
     SkMatrix shadowTransform;
     if (!ctm.hasPerspective()) {
         SkScalar scale;
         SkVector translate;
-        SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
-                                           lightRadius, &radius, &scale, &translate);
+        SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, lightPos.fZ,
+                                           lightRadius, &outset, &scale, &translate);
         shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
         shadowTransform.preConcat(ctm);
     } else {
@@ -866,7 +990,7 @@
 
         // project from light through corners to z=0 plane
         for (int i = 0; i < 4; ++i) {
-            SkScalar zRatio = pts3D[i].fZ / (fLightZ - pts3D[i].fZ);
+            SkScalar zRatio = pts3D[i].fZ / (lightPos.fZ - pts3D[i].fZ);
             pts3D[i].fX -= (lightPos.fX - pts3D[i].fX)*zRatio;
             pts3D[i].fY -= (lightPos.fY - pts3D[i].fY)*zRatio;
             pts3D[i].fZ = SK_Scalar1;
@@ -907,9 +1031,9 @@
                              0, 0, 1);
         shadowTransform.preConcat(toHomogeneous);
 
-        radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
+        outset = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
     }
-    fRadius = radius;
+    SkScalar inset = outset;
 
     // compute rough clip bounds for umbra, plus offset polygon, plus centroid
     if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
@@ -921,10 +1045,16 @@
         return;
     }
 
-    // compute vectors for clip tests
-    this->computeClipVectorsAndTestCentroid();
+    // TODO: calculate these reserves better
+    // Penumbra ring: 3*numPts
+    // Umbra ring: numPts
+    // Inner ring: numPts
+    fPositions.setReserve(5 * path.countPoints());
+    fColors.setReserve(5 * path.countPoints());
+    // Penumbra ring: 12*numPts
+    // Umbra ring: 3*numPts
+    fIndices.setReserve(15 * path.countPoints());
 
-    // check to see if umbra collapses
     if (fIsConvex) {
         SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
                                                                           fPathPolygon[0],
@@ -945,31 +1075,17 @@
             }
         }
         static constexpr auto kTolerance = 1.0e-2f;
-        if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
+        if (minDistSq < (inset + kTolerance)*(inset + kTolerance)) {
             // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
-            SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
-            fOffsetAdjust = newRadius - radius;
-            SkScalar ratio = 128 * (newRadius + radius) / radius;
+            SkScalar newInset = SkScalarSqrt(minDistSq) - kTolerance;
+            SkScalar ratio = 128 * (newInset + inset) / inset;
             // they aren't PMColors, but the interpolation algorithm is the same
             fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
-            radius = newRadius;
+            inset = newInset;
         }
-    }
-
-    // TODO: calculate these reserves better
-    // Penumbra ring: 3*numPts
-    // Umbra ring: numPts
-    // Inner ring: numPts
-    fPositions.setReserve(5 * path.countPoints());
-    fColors.setReserve(5 * path.countPoints());
-    // Penumbra ring: 12*numPts
-    // Umbra ring: 3*numPts
-    fIndices.setReserve(15 * path.countPoints());
-
-    if (fIsConvex) {
-        fSucceeded = this->computeConvexShadow(radius);
+        fSucceeded = this->computeConvexShadow(inset, outset, true);
     } else {
-        fSucceeded = this->computeConcaveShadow(radius);
+        fSucceeded = this->computeConcaveShadow(inset, outset);
     }
 
     if (!fSucceeded) {
@@ -979,12 +1095,6 @@
     fSucceeded = true;
 }
 
-void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
-    if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count()-1])) {
-        *fClipPolygon.push() = point;
-    }
-}
-
 bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
                                                          const SkMatrix& shadowTransform) {
 
@@ -1072,319 +1182,13 @@
     }
 
     this->finishPathPolygon();
-    fCurrClipPoint = fClipPolygon.count() - 1;
     return true;
 }
 
-void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
-    SkASSERT(fClipPolygon.count() >= 3);
-
-    // init clip vectors
-    SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
-    SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
-    *fClipVectors.push() = v0;
-
-    // init centroid check
-    bool hiddenCentroid = true;
-    v1 = fCentroid - fClipPolygon[0];
-    SkScalar initCross = v0.cross(v1);
-
-    for (int p = 1; p < fClipPolygon.count(); ++p) {
-        // add to clip vectors
-        v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
-        *fClipVectors.push() = v0;
-        // Determine if transformed centroid is inside clipPolygon.
-        v1 = fCentroid - fClipPolygon[p];
-        if (initCross*v0.cross(v1) <= 0) {
-            hiddenCentroid = false;
-        }
+void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
+    if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count() - 1])) {
+        fClipPolygon.push_back(point);
     }
-    SkASSERT(fClipVectors.count() == fClipPolygon.count());
-
-    fTransparent = fTransparent || !hiddenCentroid;
-}
-
-bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
-                                             SkPoint* clipPoint) {
-    SkVector segmentVector = centroid - umbraPoint;
-
-    int startClipPoint = fCurrClipPoint;
-    do {
-        SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
-        SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
-        SkScalar t_num = dp.cross(segmentVector);
-        // if line segments are nearly parallel
-        if (SkScalarNearlyZero(denom)) {
-            // and collinear
-            if (SkScalarNearlyZero(t_num)) {
-                return false;
-            }
-            // otherwise are separate, will try the next poly segment
-        // else if crossing lies within poly segment
-        } else if (t_num >= 0 && t_num <= denom) {
-            SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
-            // if umbra point is inside the clip polygon
-            if (s_num >= 0 && s_num <= denom) {
-                segmentVector *= s_num/denom;
-                *clipPoint = umbraPoint + segmentVector;
-                return true;
-            }
-        }
-        fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
-    } while (fCurrClipPoint != startClipPoint);
-
-    return false;
-}
-
-int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
-    SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
-    int index = fCurrUmbraPoint;
-    int dir = 1;
-    int next = (index + dir) % fUmbraPolygon.count();
-
-    // init travel direction
-    SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
-    if (distance < minDistance) {
-        index = next;
-        minDistance = distance;
-    } else {
-        dir = fUmbraPolygon.count()-1;
-    }
-
-    // iterate until we find a point that increases the distance
-    next = (index + dir) % fUmbraPolygon.count();
-    distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
-    while (distance < minDistance) {
-        index = next;
-        minDistance = distance;
-        next = (index + dir) % fUmbraPolygon.count();
-        distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
-    }
-
-    fCurrUmbraPoint = index;
-    return index;
-}
-
-bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
-    // generate inner ring
-    if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
-                              &fUmbraPolygon)) {
-        // this shouldn't happen, but just in case we'll inset using the centroid
-        fValidUmbra = false;
-    }
-
-    // walk around the path polygon, generate outer ring and connect to inner ring
-    if (fTransparent) {
-        *fPositions.push() = fCentroid;
-        *fColors.push() = fUmbraColor;
-    }
-    fCurrUmbraPoint = 0;
-
-    // initial setup
-    // add first quad
-    int polyCount = fPathPolygon.count();
-    if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &fFirstOutset)) {
-        // polygon should be sanitized by this point, so this is unrecoverable
-        return false;
-    }
-
-    fFirstOutset *= fRadius;
-    fFirstPoint = fPathPolygon[polyCount - 1];
-    fFirstVertexIndex = fPositions.count();
-    fPrevOutset = fFirstOutset;
-    fPrevPoint = fFirstPoint;
-    fPrevUmbraIndex = -1;
-
-    this->addInnerPoint(fFirstPoint, &fPrevUmbraIndex);
-
-    if (!fTransparent) {
-        SkPoint clipPoint;
-        bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
-                                              fCentroid, &clipPoint);
-        if (isOutside) {
-            *fPositions.push() = clipPoint;
-            *fColors.push() = fUmbraColor;
-        }
-        fPrevUmbraOutside = isOutside;
-        fFirstUmbraOutside = isOutside;
-    }
-
-    SkPoint newPoint = fFirstPoint + fFirstOutset;
-    *fPositions.push() = newPoint;
-    *fColors.push() = fPenumbraColor;
-    this->addEdge(fPathPolygon[0], fFirstOutset, false);
-
-    for (int i = 1; i < polyCount; ++i) {
-        if (!this->handlePolyPoint(fPathPolygon[i], i == polyCount-1)) {
-            return false;
-        }
-    }
-    SkASSERT(this->indexCount());
-
-    // final fan
-    SkASSERT(fPositions.count() >= 3);
-    if (this->addArc(fFirstOutset, false)) {
-        if (fFirstUmbraOutside) {
-            this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
-                                    fFirstVertexIndex + 2);
-        } else {
-            this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
-                                    fFirstVertexIndex + 1);
-        }
-    } else {
-        // no arc added, fix up by setting first penumbra point position to last one
-        if (fFirstUmbraOutside) {
-            fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
-        } else {
-            fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
-        }
-    }
-
-    return true;
-}
-
-bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
-    if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
-        return false;
-    }
-
-    // generate inner ring
-    SkTDArray<int> umbraIndices;
-    umbraIndices.setReserve(fPathPolygon.count());
-    if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
-                               &fUmbraPolygon, &umbraIndices)) {
-        // TODO: figure out how to handle this case
-        return false;
-    }
-
-    // generate outer ring
-    SkTDArray<SkPoint> penumbraPolygon;
-    SkTDArray<int> penumbraIndices;
-    penumbraPolygon.setReserve(fUmbraPolygon.count());
-    penumbraIndices.setReserve(fUmbraPolygon.count());
-    if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -radius,
-                               &penumbraPolygon, &penumbraIndices)) {
-        // TODO: figure out how to handle this case
-        return false;
-    }
-
-    if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
-        return false;
-    }
-
-    // attach the rings together
-    this->stitchConcaveRings(fUmbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
-
-    return true;
-}
-
-void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
-                                        SkPoint* pts, int count) {
-    // TODO: vectorize
-    for (int i = 0; i < count; ++i) {
-        pts[i] *= scale;
-        pts[i] += xlate;
-    }
-}
-
-bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p, bool lastPoint) {
-    SkVector normal;
-    if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
-        normal *= fRadius;
-        this->addArc(normal, true);
-        this->addEdge(p, normal, lastPoint);
-    }
-
-    return true;
-}
-
-bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex) {
-    SkPoint umbraPoint;
-    if (!fValidUmbra) {
-        SkVector v = fCentroid - pathPoint;
-        v *= 0.95f;
-        umbraPoint = pathPoint + v;
-    } else {
-        umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
-    }
-
-    fPrevPoint = pathPoint;
-
-    // merge "close" points
-    if (fPrevUmbraIndex == -1 ||
-        !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
-        // if we've wrapped around, don't add a new point
-        if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
-            *currUmbraIndex = fFirstVertexIndex;
-        } else {
-            *currUmbraIndex = fPositions.count();
-            *fPositions.push() = umbraPoint;
-            *fColors.push() = fUmbraColor;
-        }
-        return false;
-    } else {
-        *currUmbraIndex = fPrevUmbraIndex;
-        return true;
-    }
-}
-
-void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
-                                      bool lastEdge) {
-    // add next umbra point
-    int currUmbraIndex;
-    bool duplicate;
-    if (lastEdge) {
-        duplicate = false;
-        currUmbraIndex = fFirstVertexIndex;
-        fPrevPoint = nextPoint;
-    } else {
-        duplicate = this->addInnerPoint(nextPoint, &currUmbraIndex);
-    }
-    int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
-                          ? fPositions.count()-1
-                          : fPositions.count()-2;
-    if (!duplicate) {
-        // add to center fan if transparent or centroid showing
-        if (fTransparent) {
-            this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
-        // otherwise add to clip ring
-        } else {
-            SkPoint clipPoint;
-            bool isOutside = lastEdge ? fFirstUmbraOutside
-                                      : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
-                                                             &clipPoint);
-            if (isOutside) {
-                if (!lastEdge) {
-                    *fPositions.push() = clipPoint;
-                    *fColors.push() = fUmbraColor;
-                }
-                this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
-                if (fPrevUmbraOutside) {
-                    // fill out quad
-                    this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
-                                         fPrevUmbraIndex + 1);
-                }
-            } else if (fPrevUmbraOutside) {
-                // add tri
-                this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
-            }
-
-            fPrevUmbraOutside = isOutside;
-        }
-    }
-
-    // add next penumbra point and quad
-    SkPoint newPoint = nextPoint + nextNormal;
-    *fPositions.push() = newPoint;
-    *fColors.push() = fPenumbraColor;
-
-    if (!duplicate) {
-        this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
-    }
-    this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
-
-    fPrevUmbraIndex = currUmbraIndex;
-    fPrevOutset = nextNormal;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////