| Index: src/pathops/SkPathOpsTSect.h
|
| diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4e7d3b1795c765ae964391e88990d1d1d548dae3
|
| --- /dev/null
|
| +++ b/src/pathops/SkPathOpsTSect.h
|
| @@ -0,0 +1,1211 @@
|
| +/*
|
| + * Copyright 2014 Google Inc.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +#include "SkChunkAlloc.h"
|
| +#include "SkPathOpsRect.h"
|
| +#include "SkPathOpsQuad.h"
|
| +#include "SkIntersections.h"
|
| +#include "SkTArray.h"
|
| +
|
| +/* TCurve is either SkDQuadratic or SkDCubic */
|
| +template<typename TCurve>
|
| +class SkTCoincident {
|
| +public:
|
| + bool isCoincident() const {
|
| + return fCoincident;
|
| + }
|
| +
|
| + void init() {
|
| + fCoincident = false;
|
| + SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
|
| + SkDEBUGCODE(fPerpT = SK_ScalarNaN);
|
| + }
|
| +
|
| + void markCoincident() {
|
| + if (!fCoincident) {
|
| + fPerpT = -1;
|
| + }
|
| + fCoincident = true;
|
| + }
|
| +
|
| + const SkDPoint& perpPt() const {
|
| + return fPerpPt;
|
| + }
|
| +
|
| + double perpT() const {
|
| + return fPerpT;
|
| + }
|
| +
|
| + void setPerp(const TCurve& c1, double t, const SkDPoint& cPt, const TCurve& );
|
| +
|
| +private:
|
| + SkDPoint fPerpPt;
|
| + double fPerpT; // perpendicular intersection on opposite curve
|
| + bool fCoincident;
|
| +};
|
| +
|
| +template<typename TCurve> class SkTSect;
|
| +
|
| +/* Curve is either TCurve or SkDCubic */
|
| +template<typename TCurve>
|
| +class SkTSpan {
|
| +public:
|
| + void init(const TCurve& );
|
| + void initBounds(const TCurve& );
|
| +
|
| + double closestBoundedT(const SkDPoint& pt) const;
|
| +
|
| + bool contains(double t) const {
|
| + return !! const_cast<SkTSpan*>(this)->innerFind(t);
|
| + }
|
| +
|
| + bool contains(const SkTSpan* span) const;
|
| +
|
| + double endT() const {
|
| + return fEndT;
|
| + }
|
| +
|
| + SkTSpan* find(double t) {
|
| + SkTSpan* result = innerFind(t);
|
| + SkASSERT(result);
|
| + return result;
|
| + }
|
| +
|
| + bool intersects(const SkTSpan* span, bool* check);
|
| +
|
| + const SkTSpan* next() const {
|
| + return fNext;
|
| + }
|
| +
|
| + const TCurve& part() const {
|
| + return fPart;
|
| + }
|
| +
|
| + void reset() {
|
| + fBounded.reset();
|
| + }
|
| +
|
| + bool split(SkTSpan* work) {
|
| + return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
|
| + }
|
| +
|
| + bool splitAt(SkTSpan* work, double t);
|
| +
|
| + double startT() const {
|
| + return fStartT;
|
| + }
|
| +
|
| + bool tightBoundsIntersects(const SkTSpan* span) const;
|
| +
|
| + // implementation is for testing only
|
| + void dump() const {
|
| + dump(NULL);
|
| + }
|
| +
|
| +private:
|
| + SkTSpan* innerFind(double t);
|
| + bool linearIntersects(const TCurve& ) const;
|
| +
|
| + // implementation is for testing only
|
| +#if DEBUG_T_SECT
|
| + int debugID(const SkTSect<TCurve>* ) const { return fDebugID; }
|
| +#else
|
| + int debugID(const SkTSect<TCurve>* ) const;
|
| +#endif
|
| + void dump(const SkTSect<TCurve>* ) const;
|
| + void dumpID(const SkTSect<TCurve>* ) const;
|
| +
|
| +#if DEBUG_T_SECT
|
| + void validate() const;
|
| +#endif
|
| +
|
| + TCurve fPart;
|
| + SkTCoincident<TCurve> fCoinStart;
|
| + SkTCoincident<TCurve> fCoinEnd;
|
| + SkSTArray<4, SkTSpan*, true> fBounded;
|
| + SkTSpan* fPrev;
|
| + SkTSpan* fNext;
|
| + SkDRect fBounds;
|
| + double fStartT;
|
| + double fEndT;
|
| + double fBoundsMax;
|
| + bool fCollapsed;
|
| + bool fHasPerp;
|
| + bool fIsLinear;
|
| +#if DEBUG_T_SECT
|
| + int fDebugID;
|
| + bool fDebugDeleted;
|
| +#endif
|
| + friend class SkTSect<TCurve>;
|
| +};
|
| +
|
| +template<typename TCurve>
|
| +class SkTSect {
|
| +public:
|
| + SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id));
|
| + static void BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections);
|
| +
|
| + // for testing only
|
| + void dump() const;
|
| + void dumpBoth(const SkTSect& opp) const;
|
| + void dumpBoth(const SkTSect* opp) const;
|
| + void dumpCurves() const;
|
| +
|
| +private:
|
| + enum {
|
| + kZeroS1Set = 1,
|
| + kOneS1Set = 2,
|
| + kZeroS2Set = 4,
|
| + kOneS2Set = 8
|
| + };
|
| +
|
| + SkTSpan<TCurve>* addOne();
|
| + bool binarySearchCoin(const SkTSect& , double tStart, double tStep, double* t, double* oppT);
|
| + SkTSpan<TCurve>* boundsMax() const;
|
| + void coincidentCheck(SkTSect* sect2);
|
| + static int EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* );
|
| + bool intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
|
| + const SkTSpan<TCurve>* oppSpan) const;
|
| + void onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
|
| + void recoverCollapsed();
|
| + void removeSpan(SkTSpan<TCurve>* span);
|
| + void removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span);
|
| + void removeSpans(SkTSpan<TCurve>* span, SkTSect* opp);
|
| + void setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
|
| + const SkTSpan<TCurve>* tail() const;
|
| + void trim(SkTSpan<TCurve>* span, SkTSect* opp);
|
| +
|
| +#if DEBUG_T_SECT
|
| + int debugID() const { return fDebugID; }
|
| + void validate() const;
|
| +#else
|
| + int debugID() const { return 0; }
|
| +#endif
|
| + const TCurve& fCurve;
|
| + SkChunkAlloc fHeap;
|
| + SkTSpan<TCurve>* fHead;
|
| + SkTSpan<TCurve>* fDeleted;
|
| + int fActiveCount;
|
| +#if DEBUG_T_SECT
|
| + int fDebugID;
|
| + int fDebugCount;
|
| + int fDebugAllocatedCount;
|
| +#endif
|
| + friend class SkTSpan<TCurve>; // only used by debug id
|
| +};
|
| +
|
| +#define COINCIDENT_SPAN_COUNT 9
|
| +
|
| +template<typename TCurve>
|
| +void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
|
| + const SkDPoint& cPt, const TCurve& c2) {
|
| + SkDVector dxdy = c1.dxdyAtT(t);
|
| + SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
|
| + SkIntersections i;
|
| + int used = i.intersectRay(c2, perp);
|
| + // only keep closest
|
| + if (used == 0) {
|
| + fPerpT = -1;
|
| + return;
|
| + }
|
| + fPerpT = i[0][0];
|
| + fPerpPt = i.pt(0);
|
| + SkASSERT(used <= 2);
|
| + if (used == 2) {
|
| + double distSq = (fPerpPt - cPt).lengthSquared();
|
| + double dist2Sq = (i.pt(1) - cPt).lengthSquared();
|
| + if (dist2Sq < distSq) {
|
| + fPerpT = i[0][1];
|
| + fPerpPt = i.pt(1);
|
| + }
|
| + }
|
| + fCoincident = cPt.approximatelyEqual(fPerpPt);
|
| +#if DEBUG_T_SECT
|
| + if (fCoincident) {
|
| + SkDebugf(""); // allow setting breakpoint
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +void SkTSpan<TCurve>::init(const TCurve& c) {
|
| + fPrev = fNext = NULL;
|
| + fIsLinear = false;
|
| + fStartT = 0;
|
| + fEndT = 1;
|
| + initBounds(c);
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +void SkTSpan<TCurve>::initBounds(const TCurve& c) {
|
| + fPart = c.subDivide(fStartT, fEndT);
|
| + fBounds.setBounds(fPart);
|
| + fCoinStart.init();
|
| + fCoinEnd.init();
|
| + fBoundsMax = SkTMax(fBounds.width(), fBounds.height());
|
| + fCollapsed = fPart.collapsed();
|
| + fHasPerp = false;
|
| +#if DEBUG_T_SECT
|
| + fDebugDeleted = false;
|
| + if (fCollapsed) {
|
| + SkDebugf(""); // for convenient breakpoints
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +double SkTSpan<TCurve>::closestBoundedT(const SkDPoint& pt) const {
|
| + int count = fBounded.count();
|
| + double result = -1;
|
| + double closest = FLT_MAX;
|
| + for (int index = 0; index < count; ++index) {
|
| + const SkTSpan* test = fBounded[index];
|
| + double startDist = test->fPart[0].distanceSquared(pt);
|
| + if (closest > startDist) {
|
| + closest = startDist;
|
| + result = test->fStartT;
|
| + }
|
| + double endDist = test->fPart[TCurve::kPointLast].distanceSquared(pt);
|
| + if (closest > endDist) {
|
| + closest = endDist;
|
| + result = test->fEndT;
|
| + }
|
| + }
|
| + SkASSERT(between(0, result, 1));
|
| + return result;
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +bool SkTSpan<TCurve>::contains(const SkTSpan* span) const {
|
| + int count = fBounded.count();
|
| + for (int index = 0; index < count; ++index) {
|
| + const SkTSpan* test = fBounded[index];
|
| + if (span == test) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +SkTSpan<TCurve>* SkTSpan<TCurve>::innerFind(double t) {
|
| + SkTSpan* work = this;
|
| + do {
|
| + if (between(work->fStartT, t, work->fEndT)) {
|
| + return work;
|
| + }
|
| + } while ((work = work->fNext));
|
| + return NULL;
|
| +}
|
| +
|
| +// OPTIMIZE ? If at_most_end_pts_in_common detects that one quad is near linear,
|
| +// use line intersection to guess a better split than 0.5
|
| +// OPTIMIZE Once at_most_end_pts_in_common detects linear, mark span so all future splits are linear
|
| +template<typename TCurve>
|
| +bool SkTSpan<TCurve>::intersects(const SkTSpan* span, bool* check) {
|
| + if (!fBounds.intersects(span->fBounds)) {
|
| + *check = false; // no need to check to see if the bounds have end points in common
|
| + return false;
|
| + }
|
| + if (!fIsLinear && fPart.hullIntersects(span->fPart, check)) {
|
| + if (!*check) {
|
| + return true;
|
| + }
|
| + fIsLinear = true;
|
| + }
|
| + if (fIsLinear) {
|
| + *check = false;
|
| + return linearIntersects(span->fPart);
|
| + }
|
| + return *check;
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +bool SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
|
| + // looks like q1 is near-linear
|
| + int start = 0, end = TCurve::kPointCount - 1; // the outside points are usually the extremes
|
| + if (!fPart.controlsInside()) {
|
| + double dist = 0; // if there's any question, compute distance to find best outsiders
|
| + for (int outer = 0; outer < TCurve::kPointCount - 1; ++outer) {
|
| + for (int inner = outer + 1; inner < TCurve::kPointCount; ++inner) {
|
| + double test = (fPart[outer] - fPart[inner]).lengthSquared();
|
| + if (dist > test) {
|
| + continue;
|
| + }
|
| + dist = test;
|
| + start = outer;
|
| + end = inner;
|
| + }
|
| + }
|
| + }
|
| + // see if q2 is on one side of the line formed by the extreme points
|
| + double origX = fPart[start].fX;
|
| + double origY = fPart[start].fY;
|
| + double adj = fPart[end].fX - origX;
|
| + double opp = fPart[end].fY - origY;
|
| + double sign;
|
| + for (int n = 0; n < TCurve::kPointCount; ++n) {
|
| + double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
|
| + if (precisely_zero(test)) {
|
| + return true;
|
| + }
|
| + if (n == 0) {
|
| + sign = test;
|
| + continue;
|
| + }
|
| + if (test * sign < 0) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t) {
|
| + fStartT = t;
|
| + fEndT = work->fEndT;
|
| + if (fStartT == fEndT) {
|
| + fCollapsed = true;
|
| + return false;
|
| + }
|
| + work->fEndT = t;
|
| + if (work->fStartT == work->fEndT) {
|
| + work->fCollapsed = true;
|
| + return false;
|
| + }
|
| + fPrev = work;
|
| + fNext = work->fNext;
|
| + fIsLinear = work->fIsLinear;
|
| + work->fNext = this;
|
| + if (fNext) {
|
| + fNext->fPrev = this;
|
| + }
|
| + fBounded = work->fBounded;
|
| + int count = fBounded.count();
|
| + for (int index = 0; index < count; ++index) {
|
| + fBounded[index]->fBounded.push_back() = this;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +bool SkTSpan<TCurve>::tightBoundsIntersects(const SkTSpan* span) const {
|
| + // skew all to an axis
|
| + SkDVector v2_0 = fPart[TCurve::kPointLast] - fPart[0];
|
| + bool skewToXAxis = fabs(v2_0.fX) > fabs(v2_0.fY);
|
| + double ratio = skewToXAxis ? v2_0.fY / v2_0.fX : v2_0.fX / v2_0.fY;
|
| + TCurve r1 = fPart;
|
| + if (skewToXAxis) {
|
| + r1[1].fY -= (fPart[1].fX - r1[0].fX) * ratio;
|
| + if (TCurve::IsCubic()) {
|
| + r1[2].fY -= (fPart[2].fX - r1[0].fX) * ratio;
|
| + r1[3].fY = r1[0].fY;
|
| + } else {
|
| + r1[2].fY = r1[0].fY;
|
| + }
|
| + } else {
|
| + r1[1].fX -= (fPart[1].fY - r1[0].fY) * ratio;
|
| + if (TCurve::IsCubic()) {
|
| + r1[2].fX -= (fPart[2].fY - r1[0].fY) * ratio;
|
| + r1[3].fX = r1[0].fX;
|
| + } else {
|
| + r1[2].fX = r1[0].fX;
|
| + }
|
| + }
|
| + // compute the tight skewed bounds
|
| + SkDRect bounds;
|
| + bounds.setBounds(r1);
|
| + // see if opposite ends are within range of tight skewed bounds
|
| + TCurve r2 = span->fPart;
|
| + for (int i = 0; i < TCurve::kPointCount; i += 2) {
|
| + if (skewToXAxis) {
|
| + r2[i].fY -= (r2[i].fX - r1[0].fX) * ratio;
|
| + if (between(bounds.fTop, r2[i].fY, bounds.fBottom)) {
|
| + return true;
|
| + }
|
| + } else {
|
| + r2[i].fX -= (r2[i].fY - r1[0].fY) * ratio;
|
| + if (between(bounds.fLeft, r2[i].fX, bounds.fRight)) {
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + // see if opposite ends are on either side of tight skewed bounds
|
| + if ((skewToXAxis ? (r2[0].fY - r1[0].fY) * (r2[TCurve::kPointLast].fY - r1[0].fY)
|
| + : (r2[0].fX - r1[0].fX) * (r2[TCurve::kPointLast].fX - r1[0].fX)) < 0) {
|
| + return true;
|
| + }
|
| + // compute opposite tight skewed bounds
|
| + if (skewToXAxis) {
|
| + r2[1].fY -= (r2[1].fX - r1[0].fX) * ratio;
|
| + if (TCurve::IsCubic()) {
|
| + r2[2].fY -= (r2[2].fX - r1[0].fX) * ratio;
|
| + }
|
| + } else {
|
| + r2[1].fX -= (r2[1].fY - r1[0].fY) * ratio;
|
| + if (TCurve::IsCubic()) {
|
| + r2[2].fX -= (r2[2].fY - r1[0].fY) * ratio;
|
| + }
|
| + }
|
| + SkDRect sBounds;
|
| + sBounds.setBounds(r2);
|
| + // see if tight bounds overlap
|
| + if (skewToXAxis) {
|
| + return bounds.fTop <= sBounds.fBottom && sBounds.fTop <= bounds.fBottom;
|
| + } else {
|
| + return bounds.fLeft <= sBounds.fRight && sBounds.fLeft <= bounds.fRight;
|
| + }
|
| +}
|
| +
|
| +#if DEBUG_T_SECT
|
| +template<typename TCurve>
|
| +void SkTSpan<TCurve>::validate() const {
|
| + SkASSERT(fNext == NULL || fNext != fPrev);
|
| + SkASSERT(fNext == NULL || this == fNext->fPrev);
|
| + SkASSERT(fBounds.width() || fBounds.height());
|
| + SkASSERT(fBoundsMax == SkTMax(fBounds.width(), fBounds.height()));
|
| + SkASSERT(0 <= fStartT);
|
| + SkASSERT(fEndT <= 1);
|
| + SkASSERT(fStartT < fEndT);
|
| + SkASSERT(fBounded.count() > 0);
|
| + for (int index = 0; index < fBounded.count(); ++index) {
|
| + const SkTSpan* overlap = fBounded[index];
|
| + SkASSERT(((fDebugID ^ overlap->fDebugID) & 1) == 1);
|
| + SkASSERT(overlap->contains(this));
|
| + }
|
| +}
|
| +#endif
|
| +
|
| +template<typename TCurve>
|
| +SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id))
|
| + : fCurve(c)
|
| + , fHeap(sizeof(SkTSpan<TCurve>) * 4)
|
| + , fDeleted(NULL)
|
| + , fActiveCount(0)
|
| + PATH_OPS_DEBUG_PARAMS(fDebugID(id))
|
| + PATH_OPS_DEBUG_PARAMS(fDebugCount(0))
|
| + PATH_OPS_DEBUG_PARAMS(fDebugAllocatedCount(0))
|
| +{
|
| + fHead = addOne();
|
| + fHead->init(c);
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +SkTSpan<TCurve>* SkTSect<TCurve>::addOne() {
|
| + SkTSpan<TCurve>* result;
|
| + if (fDeleted) {
|
| + result = fDeleted;
|
| + result->reset();
|
| + fDeleted = result->fNext;
|
| + } else {
|
| + result = SkNEW_PLACEMENT(fHeap.allocThrow(sizeof(SkTSpan<TCurve>)), SkTSpan<TCurve>);
|
| +#if DEBUG_T_SECT
|
| + ++fDebugAllocatedCount;
|
| +#endif
|
| + }
|
| + ++fActiveCount;
|
| +#if DEBUG_T_SECT
|
| + result->fDebugID = fDebugCount++ * 2 + fDebugID;
|
| +#endif
|
| + return result;
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +bool SkTSect<TCurve>::binarySearchCoin(const SkTSect& sect2, double tStart, double tStep,
|
| + double* resultT, double* oppT) {
|
| + SkTSpan<TCurve> work;
|
| + double result = work.fStartT = work.fEndT = tStart;
|
| + SkDPoint last = fCurve.ptAtT(tStart);
|
| + SkDPoint oppPt;
|
| + bool flip = false;
|
| + SkDEBUGCODE(bool down = tStep < 0);
|
| + const TCurve& opp = sect2.fCurve;
|
| + do {
|
| + tStep *= 0.5;
|
| + work.fStartT += tStep;
|
| + if (flip) {
|
| + tStep = -tStep;
|
| + flip = false;
|
| + }
|
| + work.initBounds(fCurve);
|
| + if (work.fCollapsed) {
|
| + return false;
|
| + }
|
| + if (last.approximatelyEqual(work.fPart[0])) {
|
| + break;
|
| + }
|
| + last = work.fPart[0];
|
| + work.fCoinStart.setPerp(fCurve, work.fStartT, last, opp);
|
| + if (work.fCoinStart.isCoincident()) {
|
| + double oppTTest = work.fCoinStart.perpT();
|
| + if (sect2.fHead->contains(oppTTest)) {
|
| + *oppT = oppTTest;
|
| + oppPt = work.fCoinStart.perpPt();
|
| + SkASSERT(down ? result > work.fStartT : result < work.fStartT);
|
| + result = work.fStartT;
|
| + continue;
|
| + }
|
| + }
|
| + tStep = -tStep;
|
| + flip = true;
|
| + } while (true);
|
| + if (last.approximatelyEqual(fCurve[0])) {
|
| + result = 0;
|
| + } else if (last.approximatelyEqual(fCurve[TCurve::kPointLast])) {
|
| + result = 1;
|
| + }
|
| + if (oppPt.approximatelyEqual(opp[0])) {
|
| + *oppT = 0;
|
| + } else if (oppPt.approximatelyEqual(opp[TCurve::kPointLast])) {
|
| + *oppT = 1;
|
| + }
|
| + *resultT = result;
|
| + return true;
|
| +}
|
| +
|
| +// OPTIMIZE ? keep a sorted list of sizes in the form of a doubly-linked list in quad span
|
| +// so that each quad sect has a pointer to the largest, and can update it as spans
|
| +// are split
|
| +template<typename TCurve>
|
| +SkTSpan<TCurve>* SkTSect<TCurve>::boundsMax() const {
|
| + SkTSpan<TCurve>* test = fHead;
|
| + SkTSpan<TCurve>* largest = fHead;
|
| + bool largestCoin = largest->fCoinStart.isCoincident() && largest->fCoinEnd.isCoincident();
|
| + while ((test = test->fNext)) {
|
| + bool testCoin = test->fCoinStart.isCoincident() || test->fCoinEnd.isCoincident();
|
| + if ((largestCoin && !testCoin) || (largestCoin == testCoin
|
| + && (largest->fBoundsMax < test->fBoundsMax
|
| + || (largest->fCollapsed && !test->fCollapsed)))) {
|
| + largest = test;
|
| + largestCoin = testCoin;
|
| + }
|
| + }
|
| + return largestCoin ? NULL : largest;
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::coincidentCheck(SkTSect* sect2) {
|
| + SkTSpan<TCurve>* first = fHead;
|
| + SkTSpan<TCurve>* next;
|
| + do {
|
| + int consecutive = 1;
|
| + SkTSpan<TCurve>* last = first;
|
| + do {
|
| + next = last->fNext;
|
| + if (!next) {
|
| + break;
|
| + }
|
| + if (next->fStartT > last->fEndT) {
|
| + break;
|
| + }
|
| + ++consecutive;
|
| + last = next;
|
| + } while (true);
|
| + if (consecutive < COINCIDENT_SPAN_COUNT) {
|
| + continue;
|
| + }
|
| + setPerp(sect2->fCurve, first, last);
|
| + // check to see if a range of points are on the curve
|
| + onCurveCheck(sect2, first, last);
|
| + SkTSpan<TCurve>* removalCandidate = NULL;
|
| + if (!first->fCoinStart.isCoincident()) {
|
| + SkTSpan<TCurve>* firstCoin = first->fNext;
|
| + removalCandidate = first;
|
| + first = firstCoin;
|
| + }
|
| + if (!first->fCoinStart.isCoincident()) {
|
| + continue;
|
| + }
|
| + if (removalCandidate) {
|
| + removeSpans(removalCandidate, sect2);
|
| + }
|
| + if (!last->fCoinStart.isCoincident()) {
|
| + continue;
|
| + }
|
| + if (!last->fCoinEnd.isCoincident()) {
|
| + if (--consecutive < COINCIDENT_SPAN_COUNT) {
|
| + continue;
|
| + }
|
| + last = last->fPrev;
|
| + SkASSERT(last->fCoinStart.isCoincident());
|
| + SkASSERT(last->fCoinEnd.isCoincident());
|
| + }
|
| + SkASSERT(between(0, first->fCoinStart.perpT(), 1) || first->fCoinStart.perpT() == -1);
|
| + if (first->fCoinStart.perpT() < 0) {
|
| + first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve);
|
| + }
|
| + SkASSERT(between(0, last->fCoinEnd.perpT(), 1) || last->fCoinEnd.perpT() == -1);
|
| + if (last->fCoinEnd.perpT() < 0) {
|
| + last->fCoinEnd.setPerp(fCurve, last->fEndT, last->fPart[TCurve::kPointLast],
|
| + sect2->fCurve);
|
| + }
|
| + SkTSpan<TCurve>* removeMe = first->fNext;
|
| + while (removeMe != last) {
|
| + SkTSpan<TCurve>* removeNext = removeMe->fNext;
|
| + removeSpans(removeMe, sect2);
|
| + removeMe = removeNext;
|
| + }
|
| + } while ((first = next));
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +bool SkTSect<TCurve>::intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
|
| + const SkTSpan<TCurve>* oppSpan) const {
|
| + bool check; // we ignore whether the end points are in common or not
|
| + if (!span->intersects(oppSpan, &check)) {
|
| + return false;
|
| + }
|
| + if (fActiveCount < COINCIDENT_SPAN_COUNT || opp->fActiveCount < COINCIDENT_SPAN_COUNT) {
|
| + return true;
|
| + }
|
| + return span->tightBoundsIntersects(oppSpan);
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
|
| + SkTSpan<TCurve>* work = first;
|
| + first = NULL;
|
| + do {
|
| + if (work->fCoinStart.isCoincident()) {
|
| + if (!first) {
|
| + first = work;
|
| + }
|
| + } else if (first) {
|
| + break;
|
| + }
|
| + if (work == last) {
|
| + break;
|
| + }
|
| + work = work->fNext;
|
| + SkASSERT(work);
|
| + } while (true);
|
| + if (!first) {
|
| + return;
|
| + }
|
| + // march outwards to find limit of coincidence from here to previous and next spans
|
| + double startT = first->fStartT;
|
| + double oppT;
|
| + SkTSpan<TCurve>* prev = first->fPrev;
|
| + if (prev) {
|
| + double coinStart;
|
| + if (binarySearchCoin(*sect2, startT, prev->fStartT - startT, &coinStart, &oppT)) {
|
| + if (coinStart < startT) {
|
| + SkASSERT(prev->fStartT < coinStart && coinStart < prev->fEndT);
|
| + SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT);
|
| + if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) {
|
| + // split prev at coinStart if needed
|
| + SkTSpan<TCurve>* half2 = addOne();
|
| + half2->splitAt(prev, coinStart);
|
| + half2->initBounds(fCurve);
|
| + prev->initBounds(fCurve);
|
| + prev->fCoinEnd.markCoincident();
|
| + half2->fCoinStart.markCoincident();
|
| + half2->fCoinEnd.markCoincident();
|
| + // find span containing opposite t, and split that too
|
| + SkTSpan<TCurve>* oppHalf = sect2->addOne();
|
| + oppHalf->splitAt(oppStart, oppT);
|
| + oppHalf->initBounds(sect2->fCurve);
|
| + oppStart->initBounds(sect2->fCurve);
|
| + } else {
|
| + SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT);
|
| + first->fStartT = coinStart;
|
| + prev->fEndT = coinStart;
|
| + first->initBounds(fCurve);
|
| + prev->initBounds(fCurve);
|
| + first->fCoinStart.markCoincident();
|
| + first->fCoinEnd.markCoincident();
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (!work->fCoinEnd.isCoincident()) {
|
| + if (work->fEndT == 1) {
|
| + SkDebugf("!");
|
| + }
|
| +// SkASSERT(work->fEndT < 1);
|
| + startT = work->fStartT;
|
| + double coinEnd;
|
| + if (binarySearchCoin(*sect2, startT, work->fEndT - startT, &coinEnd, &oppT)) {
|
| + if (coinEnd > startT) {
|
| + SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT);
|
| + if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) {
|
| + SkASSERT(coinEnd < work->fEndT);
|
| + // split prev at coinEnd if needed
|
| + SkTSpan<TCurve>* half2 = addOne();
|
| + half2->splitAt(work, coinEnd);
|
| + half2->initBounds(fCurve);
|
| + work->initBounds(fCurve);
|
| + work->fCoinStart.markCoincident();
|
| + work->fCoinEnd.markCoincident();
|
| + half2->fCoinStart.markCoincident();
|
| + SkTSpan<TCurve>* oppHalf = sect2->addOne();
|
| + oppHalf->splitAt(oppStart, oppT);
|
| + oppHalf->initBounds(sect2->fCurve);
|
| + oppStart->initBounds(sect2->fCurve);
|
| + } else {
|
| + SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT);
|
| + SkTSpan<TCurve>* next = work->fNext;
|
| + bool hasNext = next && work->fEndT == next->fStartT;
|
| + work->fEndT = coinEnd;
|
| + work->initBounds(fCurve);
|
| + work->fCoinStart.markCoincident();
|
| + work->fCoinEnd.markCoincident();
|
| + if (hasNext) {
|
| + next->fStartT = coinEnd;
|
| + next->initBounds(fCurve);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::recoverCollapsed() {
|
| + SkTSpan<TCurve>* deleted = fDeleted;
|
| + while (deleted) {
|
| + SkTSpan<TCurve>* delNext = deleted->fNext;
|
| + if (deleted->fCollapsed) {
|
| + SkTSpan<TCurve>** spanPtr = &fHead;
|
| + while (*spanPtr && (*spanPtr)->fEndT <= deleted->fStartT) {
|
| + spanPtr = &(*spanPtr)->fNext;
|
| + }
|
| + deleted->fNext = *spanPtr;
|
| + *spanPtr = deleted;
|
| + }
|
| + deleted = delNext;
|
| + }
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) {
|
| + SkTSpan<TCurve>* prev = span->fPrev;
|
| + SkTSpan<TCurve>* next = span->fNext;
|
| + if (prev) {
|
| + prev->fNext = next;
|
| + if (next) {
|
| + next->fPrev = prev;
|
| + }
|
| + } else {
|
| + fHead = next;
|
| + if (next) {
|
| + next->fPrev = NULL;
|
| + }
|
| + }
|
| + --fActiveCount;
|
| + span->fNext = fDeleted;
|
| + fDeleted = span;
|
| +#if DEBUG_T_SECT
|
| + SkASSERT(!span->fDebugDeleted);
|
| + span->fDebugDeleted = true;
|
| +#endif
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span) {
|
| + int last = span->fBounded.count() - 1;
|
| + for (int index = 0; index <= last; ++index) {
|
| + if (span->fBounded[index] == test) {
|
| + span->fBounded.removeShuffle(index);
|
| + if (!last) {
|
| + removeSpan(span);
|
| + }
|
| + return;
|
| + }
|
| + }
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::removeSpans(SkTSpan<TCurve>* span, SkTSect<TCurve>* opp) {
|
| + int count = span->fBounded.count();
|
| + for (int index = 0; index < count; ++index) {
|
| + SkTSpan<TCurve>* bounded = span->fBounded[0];
|
| + removeOne(bounded, span); // shuffles last into position 0
|
| + opp->removeOne(span, bounded);
|
| + }
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
|
| + SkTSpan<TCurve>* work = first;
|
| + if (!work->fHasPerp) {
|
| + work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
|
| + }
|
| + do {
|
| + if (!work->fHasPerp) {
|
| + work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
|
| + work->fHasPerp = true;
|
| + }
|
| + if (work == last) {
|
| + break;
|
| + }
|
| + SkTSpan<TCurve>* last = work;
|
| + work = work->fNext;
|
| + SkASSERT(work);
|
| + if (!work->fHasPerp) {
|
| + work->fCoinStart = last->fCoinEnd;
|
| + }
|
| + } while (true);
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +const SkTSpan<TCurve>* SkTSect<TCurve>::tail() const {
|
| + const SkTSpan<TCurve>* result = fHead;
|
| + const SkTSpan<TCurve>* next = fHead;
|
| + while ((next = next->fNext)) {
|
| + if (next->fEndT > result->fEndT) {
|
| + result = next;
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +/* Each span has a range of opposite spans it intersects. After the span is split in two,
|
| + adjust the range to its new size */
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::trim(SkTSpan<TCurve>* span, SkTSect* opp) {
|
| + span->initBounds(fCurve);
|
| + int count = span->fBounded.count();
|
| + for (int index = 0; index < count; ) {
|
| + SkTSpan<TCurve>* test = span->fBounded[index];
|
| + bool sects = intersects(span, opp, test);
|
| + if (sects) {
|
| + ++index;
|
| + } else {
|
| + removeOne(test, span);
|
| + opp->removeOne(span, test);
|
| + --count;
|
| + }
|
| + }
|
| +}
|
| +
|
| +#if DEBUG_T_SECT
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::validate() const {
|
| + int count = 0;
|
| + if (fHead) {
|
| + const SkTSpan<TCurve>* span = fHead;
|
| + SkASSERT(!span->fPrev);
|
| + double last = 0;
|
| + do {
|
| + span->validate();
|
| + SkASSERT(span->fStartT >= last);
|
| + last = span->fEndT;
|
| + ++count;
|
| + } while ((span = span->fNext) != NULL);
|
| + }
|
| + SkASSERT(count == fActiveCount);
|
| + SkASSERT(fActiveCount <= fDebugAllocatedCount);
|
| + int deletedCount = 0;
|
| + const SkTSpan<TCurve>* deleted = fDeleted;
|
| + while (deleted) {
|
| + ++deletedCount;
|
| + deleted = deleted->fNext;
|
| + }
|
| + SkASSERT(fActiveCount + deletedCount == fDebugAllocatedCount);
|
| +}
|
| +#endif
|
| +
|
| +template<typename TCurve>
|
| +int SkTSect<TCurve>::EndsEqual(const SkTSect* sect1, const SkTSect* sect2,
|
| + SkIntersections* intersections) {
|
| + int zeroOneSet = 0;
|
| + // check for zero
|
| + if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
|
| + zeroOneSet |= kZeroS1Set | kZeroS2Set;
|
| + if (sect1->fCurve[0] != sect2->fCurve[0]) {
|
| + intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
|
| + } else {
|
| + intersections->insert(0, 0, sect1->fCurve[0]);
|
| + }
|
| + }
|
| + if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
|
| + zeroOneSet |= kZeroS1Set | kOneS2Set;
|
| + if (sect1->fCurve[0] != sect2->fCurve[TCurve::kPointLast]) {
|
| + intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]);
|
| + } else {
|
| + intersections->insert(0, 1, sect1->fCurve[0]);
|
| + }
|
| + }
|
| + // check for one
|
| + if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
|
| + zeroOneSet |= kOneS1Set | kZeroS2Set;
|
| + if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[0]) {
|
| + intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
|
| + } else {
|
| + intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
|
| + }
|
| + }
|
| + if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
|
| + zeroOneSet |= kOneS1Set | kOneS2Set;
|
| + if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[TCurve::kPointLast]) {
|
| + intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
|
| + sect2->fCurve[TCurve::kPointLast]);
|
| + } else {
|
| + intersections->insert(1, 1, sect1->fCurve[TCurve::kPointLast]);
|
| + }
|
| + }
|
| + return zeroOneSet;
|
| +}
|
| +
|
| +template<typename TCurve>
|
| +struct SkClosestRecord {
|
| + void addIntersection(SkIntersections* intersections) const {
|
| + double r1t = fC1Index ? fC1Span->endT() : fC1Span->startT();
|
| + double r2t = fC2Index ? fC2Span->endT() : fC2Span->startT();
|
| + intersections->insert(r1t, r2t, fC1Span->part()[fC1Index]);
|
| + }
|
| +
|
| + void findEnd(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2,
|
| + int c1Index, int c2Index) {
|
| + const TCurve& c1 = span1->part();
|
| + const TCurve& c2 = span2->part();
|
| + if (!c1[c1Index].approximatelyEqual(c2[c2Index])) {
|
| + return;
|
| + }
|
| + double dist = c1[c1Index].distanceSquared(c2[c2Index]);
|
| + if (fClosest < dist) {
|
| + return;
|
| + }
|
| + fC1Span = span1;
|
| + fC2Span = span2;
|
| + fC1StartT = span1->startT();
|
| + fC1EndT = span1->endT();
|
| + fC2StartT = span2->startT();
|
| + fC2EndT = span2->endT();
|
| + fC1Index = c1Index;
|
| + fC2Index = c2Index;
|
| + fClosest = dist;
|
| + }
|
| +
|
| + bool matesWith(const SkClosestRecord& mate) const {
|
| + SkASSERT(fC1Span == mate.fC1Span || fC1Span->endT() <= mate.fC1Span->startT()
|
| + || mate.fC1Span->endT() <= fC1Span->startT());
|
| + SkASSERT(fC2Span == mate.fC2Span || fC2Span->endT() <= mate.fC2Span->startT()
|
| + || mate.fC2Span->endT() <= fC2Span->startT());
|
| + return fC1Span == mate.fC1Span || fC1Span->endT() == mate.fC1Span->startT()
|
| + || fC1Span->startT() == mate.fC1Span->endT()
|
| + || fC2Span == mate.fC2Span
|
| + || fC2Span->endT() == mate.fC2Span->startT()
|
| + || fC2Span->startT() == mate.fC2Span->endT();
|
| + }
|
| +
|
| + void merge(const SkClosestRecord& mate) {
|
| + fC1Span = mate.fC1Span;
|
| + fC2Span = mate.fC2Span;
|
| + fClosest = mate.fClosest;
|
| + fC1Index = mate.fC1Index;
|
| + fC2Index = mate.fC2Index;
|
| + }
|
| +
|
| + void reset() {
|
| + fClosest = FLT_MAX;
|
| + SkDEBUGCODE(fC1Span = fC2Span = NULL);
|
| + SkDEBUGCODE(fC1Index = fC2Index = -1);
|
| + }
|
| +
|
| + void update(const SkClosestRecord& mate) {
|
| + fC1StartT = SkTMin(fC1StartT, mate.fC1StartT);
|
| + fC1EndT = SkTMax(fC1EndT, mate.fC1EndT);
|
| + fC2StartT = SkTMin(fC2StartT, mate.fC2StartT);
|
| + fC2EndT = SkTMax(fC2EndT, mate.fC2EndT);
|
| + }
|
| +
|
| + const SkTSpan<TCurve>* fC1Span;
|
| + const SkTSpan<TCurve>* fC2Span;
|
| + double fC1StartT;
|
| + double fC1EndT;
|
| + double fC2StartT;
|
| + double fC2EndT;
|
| + double fClosest;
|
| + int fC1Index;
|
| + int fC2Index;
|
| +};
|
| +
|
| +template<typename TCurve>
|
| +struct SkClosestSect {
|
| + SkClosestSect()
|
| + : fUsed(0) {
|
| + fClosest.push_back().reset();
|
| + }
|
| +
|
| + void find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) {
|
| + SkClosestRecord<TCurve>* record = &fClosest[fUsed];
|
| + record->findEnd(span1, span2, 0, 0);
|
| + record->findEnd(span1, span2, 0, TCurve::kPointLast);
|
| + record->findEnd(span1, span2, TCurve::kPointLast, 0);
|
| + record->findEnd(span1, span2, TCurve::kPointLast, TCurve::kPointLast);
|
| + if (record->fClosest == FLT_MAX) {
|
| + return;
|
| + }
|
| + for (int index = 0; index < fUsed; ++index) {
|
| + SkClosestRecord<TCurve>* test = &fClosest[index];
|
| + if (test->matesWith(*record)) {
|
| + if (test->fClosest > record->fClosest) {
|
| + test->merge(*record);
|
| + }
|
| + test->update(*record);
|
| + record->reset();
|
| + return;
|
| + }
|
| + }
|
| + ++fUsed;
|
| + fClosest.push_back().reset();
|
| + }
|
| +
|
| + void finish(SkIntersections* intersections) const {
|
| + for (int index = 0; index < fUsed; ++index) {
|
| + const SkClosestRecord<TCurve>& test = fClosest[index];
|
| + test.addIntersection(intersections);
|
| + }
|
| + }
|
| +
|
| + // this is oversized by one so that an extra record can merge into final one
|
| + SkSTArray<TCurve::kMaxIntersections + 1, SkClosestRecord<TCurve>, true> fClosest;
|
| + int fUsed;
|
| +};
|
| +
|
| +// returns true if the rect is too small to consider
|
| +template<typename TCurve>
|
| +void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections) {
|
| + intersections->reset();
|
| + intersections->setMax(TCurve::kMaxIntersections);
|
| + SkTSpan<TCurve>* span1 = sect1->fHead;
|
| + SkTSpan<TCurve>* span2 = sect2->fHead;
|
| + bool check;
|
| + if (!span1->intersects(span2, &check)) {
|
| + return;
|
| + }
|
| + if (check) {
|
| + (void) EndsEqual(sect1, sect2, intersections);
|
| + return;
|
| + }
|
| + span1->fBounded.push_back() = span2;
|
| + span2->fBounded.push_back() = span1;
|
| + do {
|
| + // find the largest bounds
|
| + SkTSpan<TCurve>* largest1 = sect1->boundsMax();
|
| + if (!largest1) {
|
| + break;
|
| + }
|
| + SkTSpan<TCurve>* largest2 = sect2->boundsMax();
|
| + bool split1 = !largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax
|
| + || (!largest1->fCollapsed && largest2->fCollapsed)));
|
| + // split it
|
| + SkTSect* splitSect = split1 ? sect1 : sect2;
|
| + SkTSpan<TCurve>* half1 = split1 ? largest1 : largest2;
|
| + SkASSERT(half1);
|
| + if (half1->fCollapsed) {
|
| + break;
|
| + }
|
| + // trim parts that don't intersect the opposite
|
| + SkTSpan<TCurve>* half2 = splitSect->addOne();
|
| + SkTSect* unsplitSect = split1 ? sect2 : sect1;
|
| + if (!half2->split(half1)) {
|
| + break;
|
| + }
|
| + splitSect->trim(half1, unsplitSect);
|
| + splitSect->trim(half2, unsplitSect);
|
| + // if there are 9 or more continuous spans on both sects, suspect coincidence
|
| + if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
|
| + && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
|
| + sect1->coincidentCheck(sect2);
|
| + }
|
| +#if DEBUG_T_SECT
|
| + sect1->validate();
|
| + sect2->validate();
|
| +#endif
|
| +#if DEBUG_T_SECT_DUMP > 1
|
| + sect1->dumpBoth(*sect2);
|
| +#endif
|
| + if (!sect1->fHead || !sect2->fHead) {
|
| + return;
|
| + }
|
| + } while (true);
|
| + if (sect1->fActiveCount >= 2 && sect2->fActiveCount >= 2) {
|
| + // check for coincidence
|
| + SkTSpan<TCurve>* first = sect1->fHead;
|
| + do {
|
| + if (!first->fCoinStart.isCoincident()) {
|
| + continue;
|
| + }
|
| + int spanCount = 1;
|
| + SkTSpan<TCurve>* last = first;
|
| + while (last->fCoinEnd.isCoincident()) {
|
| + SkTSpan<TCurve>* next = last->fNext;
|
| + if (!next || !next->fCoinEnd.isCoincident()) {
|
| + break;
|
| + }
|
| + last = next;
|
| + ++spanCount;
|
| + }
|
| + if (spanCount < 2) {
|
| + first = last;
|
| + continue;
|
| + }
|
| + int index = intersections->insertCoincident(first->fStartT, first->fCoinStart.perpT(),
|
| + first->fPart[0]);
|
| + if (intersections->insertCoincident(last->fEndT, last->fCoinEnd.perpT(),
|
| + last->fPart[TCurve::kPointLast]) < 0) {
|
| + intersections->clearCoincidence(index);
|
| + }
|
| + } while ((first = first->fNext));
|
| + }
|
| + int zeroOneSet = EndsEqual(sect1, sect2, intersections);
|
| + sect1->recoverCollapsed();
|
| + sect2->recoverCollapsed();
|
| + SkTSpan<TCurve>* result1 = sect1->fHead;
|
| + // check heads and tails for zero and ones and insert them if we haven't already done so
|
| + const SkTSpan<TCurve>* head1 = result1;
|
| + if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) {
|
| + const SkDPoint& start1 = sect1->fCurve[0];
|
| + double t = head1->closestBoundedT(start1);
|
| + if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
|
| + intersections->insert(0, t, start1);
|
| + }
|
| + }
|
| + const SkTSpan<TCurve>* head2 = sect2->fHead;
|
| + if (!(zeroOneSet & kZeroS2Set) && approximately_less_than_zero(head2->fStartT)) {
|
| + const SkDPoint& start2 = sect2->fCurve[0];
|
| + double t = head2->closestBoundedT(start2);
|
| + if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
|
| + intersections->insert(t, 0, start2);
|
| + }
|
| + }
|
| + const SkTSpan<TCurve>* tail1 = sect1->tail();
|
| + if (!(zeroOneSet & kOneS1Set) && approximately_greater_than_one(tail1->fEndT)) {
|
| + const SkDPoint& end1 = sect1->fCurve[TCurve::kPointLast];
|
| + double t = tail1->closestBoundedT(end1);
|
| + if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
|
| + intersections->insert(1, t, end1);
|
| + }
|
| + }
|
| + const SkTSpan<TCurve>* tail2 = sect2->tail();
|
| + if (!(zeroOneSet & kOneS2Set) && approximately_greater_than_one(tail2->fEndT)) {
|
| + const SkDPoint& end2 = sect2->fCurve[TCurve::kPointLast];
|
| + double t = tail2->closestBoundedT(end2);
|
| + if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
|
| + intersections->insert(t, 1, end2);
|
| + }
|
| + }
|
| + SkClosestSect<TCurve> closest;
|
| + do {
|
| + while (result1 && result1->fCoinStart.isCoincident() && result1->fCoinEnd.isCoincident()) {
|
| + result1 = result1->fNext;
|
| + }
|
| + if (!result1) {
|
| + break;
|
| + }
|
| + SkTSpan<TCurve>* result2 = sect2->fHead;
|
| + while (result2) {
|
| + closest.find(result1, result2);
|
| + result2 = result2->fNext;
|
| + }
|
| +
|
| + } while ((result1 = result1->fNext));
|
| + closest.finish(intersections);
|
| +}
|
|
|