blob: 074fe376549b1fd60bc4e4669c52d3ba3d23ab2f [file] [log] [blame]
/*
* 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 "SkPathOpsBounds.h"
#include "SkPathOpsRect.h"
#include "SkIntersections.h"
#include "SkTSort.h"
#ifdef SK_DEBUG
typedef uint8_t SkOpDebugBool;
#else
typedef bool SkOpDebugBool;
#endif
/* TCurve and OppCurve are one of { SkDQuadratic, SkDConic, SkDCubic } */
template<typename TCurve, typename OppCurve>
class SkTCoincident {
public:
SkTCoincident() {
this->init();
}
void debugInit() {
#ifdef SK_DEBUG
this->fPerpPt.fX = this->fPerpPt.fY = SK_ScalarNaN;
this->fPerpT = SK_ScalarNaN;
this->fCoincident = 0xFF;
#endif
}
char dumpIsCoincidentStr() const;
void dump() const;
bool isCoincident() const {
SkASSERT(!!fCoincident == fCoincident);
return SkToBool(fCoincident);
}
void init() {
fPerpT = -1;
fCoincident = false;
fPerpPt.fX = fPerpPt.fY = 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 OppCurve& );
private:
SkDPoint fPerpPt;
double fPerpT; // perpendicular intersection on opposite curve
SkOpDebugBool fCoincident;
};
template<typename TCurve, typename OppCurve> class SkTSect;
template<typename TCurve, typename OppCurve> class SkTSpan;
template<typename TCurve, typename OppCurve>
struct SkTSpanBounded {
SkTSpan<TCurve, OppCurve>* fBounded;
SkTSpanBounded* fNext;
};
/* Curve is either TCurve or SkDCubic */
template<typename TCurve, typename OppCurve>
class SkTSpan {
public:
void addBounded(SkTSpan<OppCurve, TCurve>* , SkChunkAlloc* );
double closestBoundedT(const SkDPoint& pt) const;
bool contains(double t) const;
void debugInit() {
TCurve dummy;
dummy.debugInit();
init(dummy);
initBounds(dummy);
fCoinStart.init();
fCoinEnd.init();
}
const SkTSect<OppCurve, TCurve>* debugOpp() const;
const SkTSpan* debugSpan(int ) const;
const SkTSpan* debugT(double t) const;
#ifdef SK_DEBUG
bool debugIsBefore(const SkTSpan* span) const;
#endif
void dump() const;
void dumpAll() const;
void dumpBounded(int id) const;
void dumpBounds() const;
void dumpCoin() const;
double endT() const {
return fEndT;
}
SkTSpan<OppCurve, TCurve>* findOppSpan(const SkTSpan<OppCurve, TCurve>* opp) const;
SkTSpan<OppCurve, TCurve>* findOppT(double t) const {
SkTSpan<OppCurve, TCurve>* result = oppT(t);
SkASSERT(result);
return result;
}
bool hasOppT(double t) const {
return SkToBool(oppT(t));
}
int hullsIntersect(SkTSpan<OppCurve, TCurve>* span, bool* start, bool* oppStart);
void init(const TCurve& );
void initBounds(const TCurve& );
bool isBounded() const {
return fBounded != nullptr;
}
bool linearsIntersect(SkTSpan<OppCurve, TCurve>* span);
double linearT(const SkDPoint& ) const;
void markCoincident() {
fCoinStart.markCoincident();
fCoinEnd.markCoincident();
}
const SkTSpan* next() const {
return fNext;
}
bool onlyEndPointsInCommon(const SkTSpan<OppCurve, TCurve>* opp, bool* start,
bool* oppStart, bool* ptsInCommon);
const TCurve& part() const {
return fPart;
}
bool removeAllBounded();
bool removeBounded(const SkTSpan<OppCurve, TCurve>* opp);
void reset() {
fBounded = nullptr;
}
void resetBounds(const TCurve& curve) {
fIsLinear = fIsLine = false;
initBounds(curve);
}
bool split(SkTSpan* work, SkChunkAlloc* heap) {
return splitAt(work, (work->fStartT + work->fEndT) * 0.5, heap);
}
bool splitAt(SkTSpan* work, double t, SkChunkAlloc* heap);
double startT() const {
return fStartT;
}
private:
// implementation is for testing only
int debugID() const {
return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
}
void dumpID() const;
int hullCheck(const SkTSpan<OppCurve, TCurve>* opp, bool* start, bool* oppStart);
int linearIntersects(const OppCurve& ) const;
SkTSpan<OppCurve, TCurve>* oppT(double t) const;
void validate() const;
void validateBounded() const;
void validatePerpT(double oppT) const;
void validatePerpPt(double t, const SkDPoint& ) const;
TCurve fPart;
SkTCoincident<TCurve, OppCurve> fCoinStart;
SkTCoincident<TCurve, OppCurve> fCoinEnd;
SkTSpanBounded<OppCurve, TCurve>* fBounded;
SkTSpan* fPrev;
SkTSpan* fNext;
SkDRect fBounds;
double fStartT;
double fEndT;
double fBoundsMax;
SkOpDebugBool fCollapsed;
SkOpDebugBool fHasPerp;
SkOpDebugBool fIsLinear;
SkOpDebugBool fIsLine;
SkOpDebugBool fDeleted;
SkDEBUGCODE_(SkTSect<TCurve, OppCurve>* fDebugSect);
PATH_OPS_DEBUG_T_SECT_CODE(int fID);
friend class SkTSect<TCurve, OppCurve>;
friend class SkTSect<OppCurve, TCurve>;
friend class SkTSpan<OppCurve, TCurve>;
};
template<typename TCurve, typename OppCurve>
class SkTSect {
public:
SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id));
static void BinarySearch(SkTSect* sect1, SkTSect<OppCurve, TCurve>* sect2,
SkIntersections* intersections);
// for testing only
bool debugHasBounded(const SkTSpan<OppCurve, TCurve>* ) const;
const SkTSect<OppCurve, TCurve>* debugOpp() const {
return SkDEBUGRELEASE(fOppSect, nullptr);
}
const SkTSpan<TCurve, OppCurve>* debugSpan(int id) const;
const SkTSpan<TCurve, OppCurve>* debugT(double t) const;
void dump() const;
void dumpBoth(SkTSect<OppCurve, TCurve>* ) const;
void dumpBounded(int id) const;
void dumpBounds() const;
void dumpCoin() const;
void dumpCoinCurves() const;
void dumpCurves() const;
private:
enum {
kZeroS1Set = 1,
kOneS1Set = 2,
kZeroS2Set = 4,
kOneS2Set = 8
};
SkTSpan<TCurve, OppCurve>* addFollowing(SkTSpan<TCurve, OppCurve>* prior);
void addForPerp(SkTSpan<OppCurve, TCurve>* span, double t);
SkTSpan<TCurve, OppCurve>* addOne();
SkTSpan<TCurve, OppCurve>* addSplitAt(SkTSpan<TCurve, OppCurve>* span, double t) {
SkTSpan<TCurve, OppCurve>* result = this->addOne();
result->splitAt(span, t, &fHeap);
result->initBounds(fCurve);
span->initBounds(fCurve);
return result;
}
bool binarySearchCoin(SkTSect<OppCurve, TCurve>* , double tStart, double tStep, double* t,
double* oppT);
SkTSpan<TCurve, OppCurve>* boundsMax() const;
void coincidentCheck(SkTSect<OppCurve, TCurve>* sect2);
void coincidentForce(SkTSect<OppCurve, TCurve>* sect2, double start1s, double start1e);
bool coincidentHasT(double t);
int collapsed() const;
void computePerpendiculars(SkTSect<OppCurve, TCurve>* sect2, SkTSpan<TCurve, OppCurve>* first,
SkTSpan<TCurve, OppCurve>* last);
int countConsecutiveSpans(SkTSpan<TCurve, OppCurve>* first,
SkTSpan<TCurve, OppCurve>** last) const;
int debugID() const {
return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
}
void deleteEmptySpans();
void dumpCommon(const SkTSpan<TCurve, OppCurve>* ) const;
void dumpCommonCurves(const SkTSpan<TCurve, OppCurve>* ) const;
static int EndsEqual(const SkTSect* sect1, const SkTSect<OppCurve, TCurve>* sect2,
SkIntersections* );
SkTSpan<TCurve, OppCurve>* extractCoincident(SkTSect<OppCurve, TCurve>* sect2,
SkTSpan<TCurve, OppCurve>* first,
SkTSpan<TCurve, OppCurve>* last);
SkTSpan<TCurve, OppCurve>* findCoincidentRun(SkTSpan<TCurve, OppCurve>* first,
SkTSpan<TCurve, OppCurve>** lastPtr);
int intersects(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp,
SkTSpan<OppCurve, TCurve>* oppSpan, int* oppResult);
bool isParallel(const SkDLine& thisLine, const SkTSect<OppCurve, TCurve>* opp) const;
int linesIntersect(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp,
SkTSpan<OppCurve, TCurve>* oppSpan, SkIntersections* );
void markSpanGone(SkTSpan<TCurve, OppCurve>* span);
bool matchedDirection(double t, const SkTSect<OppCurve, TCurve>* sect2, double t2) const;
void matchedDirCheck(double t, const SkTSect<OppCurve, TCurve>* sect2, double t2,
bool* calcMatched, bool* oppMatched) const;
void mergeCoincidence(SkTSect<OppCurve, TCurve>* sect2);
SkTSpan<TCurve, OppCurve>* prev(SkTSpan<TCurve, OppCurve>* ) const;
void removeByPerpendicular(SkTSect<OppCurve, TCurve>* opp);
void recoverCollapsed();
void removeCoincident(SkTSpan<TCurve, OppCurve>* span, bool isBetween);
void removeAllBut(const SkTSpan<OppCurve, TCurve>* keep, SkTSpan<TCurve, OppCurve>* span,
SkTSect<OppCurve, TCurve>* opp);
void removeSpan(SkTSpan<TCurve, OppCurve>* span);
void removeSpanRange(SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>* last);
void removeSpans(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp);
SkTSpan<TCurve, OppCurve>* spanAtT(double t, SkTSpan<TCurve, OppCurve>** priorSpan);
SkTSpan<TCurve, OppCurve>* tail();
void trim(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp);
void unlinkSpan(SkTSpan<TCurve, OppCurve>* span);
bool updateBounded(SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>* last,
SkTSpan<OppCurve, TCurve>* oppFirst);
void validate() const;
void validateBounded() const;
const TCurve& fCurve;
SkChunkAlloc fHeap;
SkTSpan<TCurve, OppCurve>* fHead;
SkTSpan<TCurve, OppCurve>* fCoincident;
SkTSpan<TCurve, OppCurve>* fDeleted;
int fActiveCount;
SkDEBUGCODE_(SkTSect<OppCurve, TCurve>* fOppSect);
PATH_OPS_DEBUG_T_SECT_CODE(int fID);
PATH_OPS_DEBUG_T_SECT_CODE(int fDebugCount);
#if DEBUG_T_SECT
int fDebugAllocatedCount;
#endif
friend class SkTSpan<TCurve, OppCurve>;
friend class SkTSpan<OppCurve, TCurve>;
friend class SkTSect<OppCurve, TCurve>;
};
#define COINCIDENT_SPAN_COUNT 9
template<typename TCurve, typename OppCurve>
void SkTCoincident<TCurve, OppCurve>::setPerp(const TCurve& c1, double t,
const SkDPoint& cPt, const OppCurve& 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 || used == 3) {
this->init();
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);
}
}
#if DEBUG_T_SECT
SkDebugf("setPerp t=%1.9g cPt=(%1.9g,%1.9g) %s oppT=%1.9g fPerpPt=(%1.9g,%1.9g)\n",
t, cPt.fX, cPt.fY,
cPt.approximatelyEqual(fPerpPt) ? "==" : "!=", fPerpT, fPerpPt.fX, fPerpPt.fY);
#endif
fCoincident = cPt.approximatelyEqual(fPerpPt);
#if DEBUG_T_SECT
if (fCoincident) {
SkDebugf(""); // allow setting breakpoint
}
#endif
}
template<typename TCurve, typename OppCurve>
void SkTSpan<TCurve, OppCurve>::addBounded(SkTSpan<OppCurve, TCurve>* span, SkChunkAlloc* heap) {
SkTSpanBounded<OppCurve, TCurve>* bounded = new (heap->allocThrow(
sizeof(SkTSpanBounded<OppCurve, TCurve>)))(SkTSpanBounded<OppCurve, TCurve>);
bounded->fBounded = span;
bounded->fNext = fBounded;
fBounded = bounded;
}
template<typename TCurve, typename OppCurve>
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::addFollowing(
SkTSpan<TCurve, OppCurve>* prior) {
SkTSpan<TCurve, OppCurve>* result = this->addOne();
result->fStartT = prior ? prior->fEndT : 0;
SkTSpan<TCurve, OppCurve>* next = prior ? prior->fNext : fHead;
result->fEndT = next ? next->fStartT : 1;
result->fPrev = prior;
result->fNext = next;
if (prior) {
prior->fNext = result;
} else {
fHead = result;
}
if (next) {
next->fPrev = result;
}
result->resetBounds(fCurve);
return result;
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::addForPerp(SkTSpan<OppCurve, TCurve>* span, double t) {
if (!span->hasOppT(t)) {
SkTSpan<TCurve, OppCurve>* priorSpan;
SkTSpan<TCurve, OppCurve>* opp = this->spanAtT(t, &priorSpan);
if (!opp) {
opp = this->addFollowing(priorSpan);
#if DEBUG_PERP
SkDebugf("%s priorSpan=%d t=%1.9g opp=%d\n", __FUNCTION__, priorSpan ?
priorSpan->debugID() : -1, t, opp->debugID());
#endif
}
#if DEBUG_PERP
opp->dump(); SkDebugf("\n");
SkDebugf("%s addBounded span=%d opp=%d\n", __FUNCTION__, priorSpan ?
priorSpan->debugID() : -1, opp->debugID());
#endif
opp->addBounded(span, &fHeap);
span->addBounded(opp, &fHeap);
}
this->validate();
#if DEBUG_T_SECT
span->validatePerpT(t);
#endif
}
template<typename TCurve, typename OppCurve>
double SkTSpan<TCurve, OppCurve>::closestBoundedT(const SkDPoint& pt) const {
double result = -1;
double closest = FLT_MAX;
const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
while (testBounded) {
const SkTSpan<OppCurve, TCurve>* test = testBounded->fBounded;
double startDist = test->fPart[0].distanceSquared(pt);
if (closest > startDist) {
closest = startDist;
result = test->fStartT;
}
double endDist = test->fPart[OppCurve::kPointLast].distanceSquared(pt);
if (closest > endDist) {
closest = endDist;
result = test->fEndT;
}
testBounded = testBounded->fNext;
}
SkASSERT(between(0, result, 1));
return result;
}
#ifdef SK_DEBUG
template<typename TCurve, typename OppCurve>
bool SkTSpan<TCurve, OppCurve>::debugIsBefore(const SkTSpan* span) const {
const SkTSpan* work = this;
do {
if (span == work) {
return true;
}
} while ((work = work->fNext));
return false;
}
#endif
template<typename TCurve, typename OppCurve>
bool SkTSpan<TCurve, OppCurve>::contains(double t) const {
const SkTSpan* work = this;
do {
if (between(work->fStartT, t, work->fEndT)) {
return true;
}
} while ((work = work->fNext));
return false;
}
template<typename TCurve, typename OppCurve>
const SkTSect<OppCurve, TCurve>* SkTSpan<TCurve, OppCurve>::debugOpp() const {
return SkDEBUGRELEASE(fDebugSect->debugOpp(), nullptr);
}
template<typename TCurve, typename OppCurve>
SkTSpan<OppCurve, TCurve>* SkTSpan<TCurve, OppCurve>::findOppSpan(
const SkTSpan<OppCurve, TCurve>* opp) const {
SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
while (bounded) {
SkTSpan<OppCurve, TCurve>* test = bounded->fBounded;
if (opp == test) {
return test;
}
bounded = bounded->fNext;
}
return nullptr;
}
// returns 0 if no hull intersection
// 1 if hulls intersect
// 2 if hulls only share a common endpoint
// -1 if linear and further checking is required
template<typename TCurve, typename OppCurve>
int SkTSpan<TCurve, OppCurve>::hullCheck(const SkTSpan<OppCurve, TCurve>* opp,
bool* start, bool* oppStart) {
if (fIsLinear) {
return -1;
}
bool ptsInCommon;
if (onlyEndPointsInCommon(opp, start, oppStart, &ptsInCommon)) {
SkASSERT(ptsInCommon);
return 2;
}
bool linear;
if (fPart.hullIntersects(opp->fPart, &linear)) {
if (!linear) { // check set true if linear
return 1;
}
fIsLinear = true;
fIsLine = fPart.controlsInside();
return ptsInCommon ? 2 : -1;
} else { // hull is not linear; check set true if intersected at the end points
return ((int) ptsInCommon) << 1; // 0 or 2
}
return 0;
}
// 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, typename OppCurve>
int SkTSpan<TCurve, OppCurve>::hullsIntersect(SkTSpan<OppCurve, TCurve>* opp,
bool* start, bool* oppStart) {
if (!fBounds.intersects(opp->fBounds)) {
return 0;
}
int hullSect = this->hullCheck(opp, start, oppStart);
if (hullSect >= 0) {
return hullSect;
}
hullSect = opp->hullCheck(this, oppStart, start);
if (hullSect >= 0) {
return hullSect;
}
return -1;
}
template<typename TCurve, typename OppCurve>
void SkTSpan<TCurve, OppCurve>::init(const TCurve& c) {
fPrev = fNext = nullptr;
fStartT = 0;
fEndT = 1;
fBounded = nullptr;
resetBounds(c);
}
template<typename TCurve, typename OppCurve>
void SkTSpan<TCurve, OppCurve>::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;
fDeleted = false;
#if DEBUG_T_SECT
if (fCollapsed) {
SkDebugf(""); // for convenient breakpoints
}
#endif
}
template<typename TCurve, typename OppCurve>
bool SkTSpan<TCurve, OppCurve>::linearsIntersect(SkTSpan<OppCurve, TCurve>* span) {
int result = this->linearIntersects(span->fPart);
if (result <= 1) {
return SkToBool(result);
}
SkASSERT(span->fIsLinear);
result = span->linearIntersects(this->fPart);
// SkASSERT(result <= 1);
return SkToBool(result);
}
template<typename TCurve, typename OppCurve>
double SkTSpan<TCurve, OppCurve>::linearT(const SkDPoint& pt) const {
SkDVector len = fPart[TCurve::kPointLast] - fPart[0];
return fabs(len.fX) > fabs(len.fY)
? (pt.fX - fPart[0].fX) / len.fX
: (pt.fY - fPart[0].fY) / len.fY;
}
template<typename TCurve, typename OppCurve>
int SkTSpan<TCurve, OppCurve>::linearIntersects(const OppCurve& q2) const {
// looks like q1 is near-linear
int start = 0, end = TCurve::kPointLast; // 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 maxPart = SkTMax(fabs(adj), fabs(opp));
double sign = 0; // initialization to shut up warning in release build
for (int n = 0; n < OppCurve::kPointCount; ++n) {
double dx = q2[n].fY - origY;
double dy = q2[n].fX - origX;
double maxVal = SkTMax(maxPart, SkTMax(fabs(dx), fabs(dy)));
double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
if (precisely_zero_when_compared_to(test, maxVal)) {
return 1;
}
if (approximately_zero_when_compared_to(test, maxVal)) {
return 3;
}
if (n == 0) {
sign = test;
continue;
}
if (test * sign < 0) {
return 1;
}
}
return 0;
}
template<typename TCurve, typename OppCurve>
bool SkTSpan<TCurve, OppCurve>::onlyEndPointsInCommon(const SkTSpan<OppCurve, TCurve>* opp,
bool* start, bool* oppStart, bool* ptsInCommon) {
if (opp->fPart[0] == fPart[0]) {
*start = *oppStart = true;
} else if (opp->fPart[0] == fPart[TCurve::kPointLast]) {
*start = false;
*oppStart = true;
} else if (opp->fPart[OppCurve::kPointLast] == fPart[0]) {
*start = true;
*oppStart = false;
} else if (opp->fPart[OppCurve::kPointLast] == fPart[TCurve::kPointLast]) {
*start = *oppStart = false;
} else {
*ptsInCommon = false;
return false;
}
*ptsInCommon = true;
const SkDPoint* otherPts[TCurve::kPointCount - 1], * oppOtherPts[OppCurve::kPointCount - 1];
int baseIndex = *start ? 0 : TCurve::kPointLast;
fPart.otherPts(baseIndex, otherPts);
opp->fPart.otherPts(*oppStart ? 0 : OppCurve::kPointLast, oppOtherPts);
const SkDPoint& base = fPart[baseIndex];
for (int o1 = 0; o1 < (int) SK_ARRAY_COUNT(otherPts); ++o1) {
SkDVector v1 = *otherPts[o1] - base;
for (int o2 = 0; o2 < (int) SK_ARRAY_COUNT(oppOtherPts); ++o2) {
SkDVector v2 = *oppOtherPts[o2] - base;
if (v2.dot(v1) >= 0) {
return false;
}
}
}
return true;
}
template<typename TCurve, typename OppCurve>
SkTSpan<OppCurve, TCurve>* SkTSpan<TCurve, OppCurve>::oppT(double t) const {
SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
while (bounded) {
SkTSpan<OppCurve, TCurve>* test = bounded->fBounded;
if (between(test->fStartT, t, test->fEndT)) {
return test;
}
bounded = bounded->fNext;
}
return nullptr;
}
template<typename TCurve, typename OppCurve>
bool SkTSpan<TCurve, OppCurve>::removeAllBounded() {
bool deleteSpan = false;
SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
while (bounded) {
SkTSpan<OppCurve, TCurve>* opp = bounded->fBounded;
deleteSpan |= opp->removeBounded(this);
bounded = bounded->fNext;
}
return deleteSpan;
}
template<typename TCurve, typename OppCurve>
bool SkTSpan<TCurve, OppCurve>::removeBounded(const SkTSpan<OppCurve, TCurve>* opp) {
if (fHasPerp) {
bool foundStart = false;
bool foundEnd = false;
SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
while (bounded) {
SkTSpan<OppCurve, TCurve>* test = bounded->fBounded;
if (opp != test) {
foundStart |= between(test->fStartT, fCoinStart.perpT(), test->fEndT);
foundEnd |= between(test->fStartT, fCoinEnd.perpT(), test->fEndT);
}
bounded = bounded->fNext;
}
if (!foundStart || !foundEnd) {
fHasPerp = false;
fCoinStart.init();
fCoinEnd.init();
}
}
SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
SkTSpanBounded<OppCurve, TCurve>* prev = nullptr;
while (bounded) {
SkTSpanBounded<OppCurve, TCurve>* boundedNext = bounded->fNext;
if (opp == bounded->fBounded) {
if (prev) {
prev->fNext = boundedNext;
return false;
} else {
fBounded = boundedNext;
return fBounded == nullptr;
}
}
prev = bounded;
bounded = boundedNext;
}
SkASSERT(0);
return false;
}
template<typename TCurve, typename OppCurve>
bool SkTSpan<TCurve, OppCurve>::splitAt(SkTSpan* work, double t, SkChunkAlloc* heap) {
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;
fIsLine = work->fIsLine;
work->fNext = this;
if (fNext) {
fNext->fPrev = this;
}
SkTSpanBounded<OppCurve, TCurve>* bounded = work->fBounded;
fBounded = nullptr;
while (bounded) {
this->addBounded(bounded->fBounded, heap);
bounded = bounded->fNext;
}
bounded = fBounded;
while (bounded) {
bounded->fBounded->addBounded(this, heap);
bounded = bounded->fNext;
}
return true;
}
template<typename TCurve, typename OppCurve>
void SkTSpan<TCurve, OppCurve>::validate() const {
#if DEBUG_T_SECT
SkASSERT(fNext == nullptr || fNext != fPrev);
SkASSERT(fNext == nullptr || this == fNext->fPrev);
SkASSERT(fPrev == nullptr || this == fPrev->fNext);
SkASSERT(fBounds.width() || fBounds.height() || fCollapsed);
SkASSERT(fBoundsMax == SkTMax(fBounds.width(), fBounds.height()));
SkASSERT(0 <= fStartT);
SkASSERT(fEndT <= 1);
SkASSERT(fStartT <= fEndT);
SkASSERT(fBounded);
this->validateBounded();
if (fHasPerp) {
if (fCoinStart.isCoincident()) {
validatePerpT(fCoinStart.perpT());
validatePerpPt(fCoinStart.perpT(), fCoinStart.perpPt());
}
if (fCoinEnd.isCoincident()) {
validatePerpT(fCoinEnd.perpT());
validatePerpPt(fCoinEnd.perpT(), fCoinEnd.perpPt());
}
}
#endif
}
template<typename TCurve, typename OppCurve>
void SkTSpan<TCurve, OppCurve>::validateBounded() const {
#if DEBUG_VALIDATE
const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
while (testBounded) {
SkDEBUGCODE_(const SkTSpan<OppCurve, TCurve>* overlap = testBounded->fBounded);
SkASSERT(!overlap->fDeleted);
SkASSERT(((this->debugID() ^ overlap->debugID()) & 1) == 1);
SkASSERT(overlap->findOppSpan(this));
testBounded = testBounded->fNext;
}
#endif
}
template<typename TCurve, typename OppCurve>
void SkTSpan<TCurve, OppCurve>::validatePerpT(double oppT) const {
const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
while (testBounded) {
const SkTSpan<OppCurve, TCurve>* overlap = testBounded->fBounded;
if (precisely_between(overlap->fStartT, oppT, overlap->fEndT)) {
return;
}
testBounded = testBounded->fNext;
}
SkASSERT(0);
}
template<typename TCurve, typename OppCurve>
void SkTSpan<TCurve, OppCurve>::validatePerpPt(double t, const SkDPoint& pt) const {
SkASSERT(fDebugSect->fOppSect->fCurve.ptAtT(t) == pt);
}
template<typename TCurve, typename OppCurve>
SkTSect<TCurve, OppCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id))
: fCurve(c)
, fHeap(sizeof(SkTSpan<TCurve, OppCurve>) * 4)
, fCoincident(nullptr)
, fDeleted(nullptr)
, fActiveCount(0)
PATH_OPS_DEBUG_T_SECT_PARAMS(fID(id))
PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugCount(0))
PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugAllocatedCount(0))
{
fHead = addOne();
fHead->init(c);
}
template<typename TCurve, typename OppCurve>
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::addOne() {
SkTSpan<TCurve, OppCurve>* result;
if (fDeleted) {
result = fDeleted;
fDeleted = result->fNext;
} else {
result = new (fHeap.allocThrow(sizeof(SkTSpan<TCurve, OppCurve>)))(
SkTSpan<TCurve, OppCurve>);
#if DEBUG_T_SECT
++fDebugAllocatedCount;
#endif
}
result->reset();
result->fHasPerp = false;
result->fDeleted = false;
++fActiveCount;
PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID);
SkDEBUGCODE(result->fDebugSect = this);
#ifdef SK_DEBUG
result->fPart.debugInit();
result->fCoinStart.debugInit();
result->fCoinEnd.debugInit();
result->fPrev = result->fNext = nullptr;
result->fBounds.debugInit();
result->fStartT = result->fEndT = result->fBoundsMax = SK_ScalarNaN;
result->fCollapsed = result->fIsLinear = result->fIsLine = 0xFF;
#endif
return result;
}
template<typename TCurve, typename OppCurve>
bool SkTSect<TCurve, OppCurve>::binarySearchCoin(SkTSect<OppCurve, TCurve>* sect2, double tStart,
double tStep, double* resultT, double* oppT) {
SkTSpan<TCurve, OppCurve> work;
double result = work.fStartT = work.fEndT = tStart;
SkDEBUGCODE(work.fDebugSect = this);
SkDPoint last = fCurve.ptAtT(tStart);
SkDPoint oppPt;
bool flip = false;
SkDEBUGCODE(bool down = tStep < 0);
const OppCurve& 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()) {
#if DEBUG_T_SECT
work.validatePerpPt(work.fCoinStart.perpT(), work.fCoinStart.perpPt());
#endif
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[OppCurve::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, typename OppCurve>
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::boundsMax() const {
SkTSpan<TCurve, OppCurve>* test = fHead;
SkTSpan<TCurve, OppCurve>* largest = fHead;
bool lCollapsed = largest->fCollapsed;
while ((test = test->fNext)) {
bool tCollapsed = test->fCollapsed;
if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed &&
largest->fBoundsMax < test->fBoundsMax)) {
largest = test;
lCollapsed = test->fCollapsed;
}
}
return largest;
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::coincidentCheck(SkTSect<OppCurve, TCurve>* sect2) {
SkTSpan<TCurve, OppCurve>* first = fHead;
SkTSpan<TCurve, OppCurve>* last, * next;
do {
int consecutive = this->countConsecutiveSpans(first, &last);
next = last->fNext;
if (consecutive < COINCIDENT_SPAN_COUNT) {
continue;
}
this->validate();
sect2->validate();
this->computePerpendiculars(sect2, first, last);
this->validate();
sect2->validate();
// check to see if a range of points are on the curve
SkTSpan<TCurve, OppCurve>* coinStart = first;
do {
coinStart = this->extractCoincident(sect2, coinStart, last);
} while (coinStart && !last->fDeleted);
} while ((first = next));
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::coincidentForce(SkTSect<OppCurve, TCurve>* sect2,
double start1s, double start1e) {
SkTSpan<TCurve, OppCurve>* first = fHead;
SkTSpan<TCurve, OppCurve>* last = this->tail();
SkTSpan<OppCurve, TCurve>* oppFirst = sect2->fHead;
SkTSpan<OppCurve, TCurve>* oppLast = sect2->tail();
bool deleteEmptySpans = this->updateBounded(first, last, oppFirst);
deleteEmptySpans |= sect2->updateBounded(oppFirst, oppLast, first);
this->removeSpanRange(first, last);
sect2->removeSpanRange(oppFirst, oppLast);
first->fStartT = start1s;
first->fEndT = start1e;
first->resetBounds(fCurve);
first->fCoinStart.setPerp(fCurve, start1s, fCurve[0], sect2->fCurve);
first->fCoinEnd.setPerp(fCurve, start1e, fCurve[TCurve::kPointLast], sect2->fCurve);
bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT();
double oppStartT = first->fCoinStart.perpT() == -1 ? 0 : SkTMax(0., first->fCoinStart.perpT());
double oppEndT = first->fCoinEnd.perpT() == -1 ? 1 : SkTMin(1., first->fCoinEnd.perpT());
if (!oppMatched) {
SkTSwap(oppStartT, oppEndT);
}
oppFirst->fStartT = oppStartT;
oppFirst->fEndT = oppEndT;
oppFirst->resetBounds(sect2->fCurve);
this->removeCoincident(first, false);
sect2->removeCoincident(oppFirst, true);
if (deleteEmptySpans) {
this->deleteEmptySpans();
sect2->deleteEmptySpans();
}
}
template<typename TCurve, typename OppCurve>
bool SkTSect<TCurve, OppCurve>::coincidentHasT(double t) {
SkTSpan<TCurve, OppCurve>* test = fCoincident;
while (test) {
if (between(test->fStartT, t, test->fEndT)) {
return true;
}
test = test->fNext;
}
return false;
}
template<typename TCurve, typename OppCurve>
int SkTSect<TCurve, OppCurve>::collapsed() const {
int result = 0;
const SkTSpan<TCurve, OppCurve>* test = fHead;
while (test) {
if (test->fCollapsed) {
++result;
}
test = test->next();
}
return result;
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::computePerpendiculars(SkTSect<OppCurve, TCurve>* sect2,
SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>* last) {
const OppCurve& opp = sect2->fCurve;
SkTSpan<TCurve, OppCurve>* work = first;
SkTSpan<TCurve, OppCurve>* prior = nullptr;
do {
if (!work->fHasPerp && !work->fCollapsed) {
if (prior) {
work->fCoinStart = prior->fCoinEnd;
} else {
work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
}
if (work->fCoinStart.isCoincident()) {
double perpT = work->fCoinStart.perpT();
if (sect2->coincidentHasT(perpT)) {
work->fCoinStart.init();
} else {
sect2->addForPerp(work, perpT);
}
}
work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
if (work->fCoinEnd.isCoincident()) {
double perpT = work->fCoinEnd.perpT();
if (sect2->coincidentHasT(perpT)) {
work->fCoinEnd.init();
} else {
sect2->addForPerp(work, perpT);
}
}
work->fHasPerp = true;
}
if (work == last) {
break;
}
prior = work;
work = work->fNext;
SkASSERT(work);
} while (true);
}
template<typename TCurve, typename OppCurve>
int SkTSect<TCurve, OppCurve>::countConsecutiveSpans(SkTSpan<TCurve, OppCurve>* first,
SkTSpan<TCurve, OppCurve>** lastPtr) const {
int consecutive = 1;
SkTSpan<TCurve, OppCurve>* last = first;
do {
SkTSpan<TCurve, OppCurve>* next = last->fNext;
if (!next) {
break;
}
if (next->fStartT > last->fEndT) {
break;
}
++consecutive;
last = next;
} while (true);
*lastPtr = last;
return consecutive;
}
template<typename TCurve, typename OppCurve>
bool SkTSect<TCurve, OppCurve>::debugHasBounded(const SkTSpan<OppCurve, TCurve>* span) const {
const SkTSpan<TCurve, OppCurve>* test = fHead;
if (!test) {
return false;
}
do {
if (test->findOppSpan(span)) {
return true;
}
} while ((test = test->next()));
return false;
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::deleteEmptySpans() {
SkTSpan<TCurve, OppCurve>* test;
SkTSpan<TCurve, OppCurve>* next = fHead;
while ((test = next)) {
next = test->fNext;
if (!test->fBounded) {
this->removeSpan(test);
}
}
}
template<typename TCurve, typename OppCurve>
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::extractCoincident(
SkTSect<OppCurve, TCurve>* sect2,
SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>* last) {
first = findCoincidentRun(first, &last);
if (!first) {
return nullptr;
}
// march outwards to find limit of coincidence from here to previous and next spans
double startT = first->fStartT;
double oppStartT SK_INIT_TO_AVOID_WARNING;
double oppEndT SK_INIT_TO_AVOID_WARNING;
SkTSpan<TCurve, OppCurve>* prev = first->fPrev;
SkASSERT(first->fCoinStart.isCoincident());
SkTSpan<OppCurve, TCurve>* oppFirst = first->findOppT(first->fCoinStart.perpT());
SkASSERT(last->fCoinEnd.isCoincident());
bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT();
double coinStart;
SkDEBUGCODE(double coinEnd);
SkTSpan<OppCurve, TCurve>* cutFirst;
if (prev && prev->fEndT == startT
&& this->binarySearchCoin(sect2, startT, prev->fStartT - startT, &coinStart,
&oppStartT)
&& prev->fStartT < coinStart && coinStart < startT
&& (cutFirst = prev->oppT(oppStartT))) {
oppFirst = cutFirst;
first = this->addSplitAt(prev, coinStart);
first->markCoincident();
prev->fCoinEnd.markCoincident();
if (oppFirst->fStartT < oppStartT && oppStartT < oppFirst->fEndT) {
SkTSpan<OppCurve, TCurve>* oppHalf = sect2->addSplitAt(oppFirst, oppStartT);
if (oppMatched) {
oppFirst->fCoinEnd.markCoincident();
oppHalf->markCoincident();
oppFirst = oppHalf;
} else {
oppFirst->markCoincident();
oppHalf->fCoinStart.markCoincident();
}
}
} else {
SkDEBUGCODE(coinStart = first->fStartT);
SkDEBUGCODE(oppStartT = oppMatched ? oppFirst->fStartT : oppFirst->fEndT);
}
// FIXME: incomplete : if we're not at the end, find end of coin
SkTSpan<OppCurve, TCurve>* oppLast;
SkASSERT(last->fCoinEnd.isCoincident());
oppLast = last->findOppT(last->fCoinEnd.perpT());
SkDEBUGCODE(coinEnd = last->fEndT);
SkDEBUGCODE(oppEndT = oppMatched ? oppLast->fEndT : oppLast->fStartT);
if (!oppMatched) {
SkTSwap(oppFirst, oppLast);
SkTSwap(oppStartT, oppEndT);
}
SkASSERT(oppStartT < oppEndT);
SkASSERT(coinStart == first->fStartT);
SkASSERT(coinEnd == last->fEndT);
SkASSERT(oppStartT == oppFirst->fStartT);
SkASSERT(oppEndT == oppLast->fEndT);
// reduce coincident runs to single entries
this->validate();
sect2->validate();
bool deleteEmptySpans = this->updateBounded(first, last, oppFirst);
deleteEmptySpans |= sect2->updateBounded(oppFirst, oppLast, first);
this->removeSpanRange(first, last);
sect2->removeSpanRange(oppFirst, oppLast);
first->fEndT = last->fEndT;
first->resetBounds(this->fCurve);
first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve);
first->fCoinEnd.setPerp(fCurve, first->fEndT, first->fPart[TCurve::kPointLast], sect2->fCurve);
oppStartT = first->fCoinStart.perpT();
oppEndT = first->fCoinEnd.perpT();
if (between(0, oppStartT, 1) && between(0, oppEndT, 1)) {
if (!oppMatched) {
SkTSwap(oppStartT, oppEndT);
}
oppFirst->fStartT = oppStartT;
oppFirst->fEndT = oppEndT;
oppFirst->resetBounds(sect2->fCurve);
}
this->validateBounded();
sect2->validateBounded();
last = first->fNext;
this->removeCoincident(first, false);
sect2->removeCoincident(oppFirst, true);
if (deleteEmptySpans) {
this->deleteEmptySpans();
sect2->deleteEmptySpans();
}
this->validate();
sect2->validate();
return last && !last->fDeleted ? last : nullptr;
}
template<typename TCurve, typename OppCurve>
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::findCoincidentRun(
SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>** lastPtr) {
SkTSpan<TCurve, OppCurve>* work = first;
SkTSpan<TCurve, OppCurve>* lastCandidate = nullptr;
first = nullptr;
// find the first fully coincident span
do {
if (work->fCoinStart.isCoincident()) {
#if DEBUG_T_SECT
work->validatePerpT(work->fCoinStart.perpT());
work->validatePerpPt(work->fCoinStart.perpT(), work->fCoinStart.perpPt());
#endif
SkASSERT(work->hasOppT(work->fCoinStart.perpT()));
if (!work->fCoinEnd.isCoincident()) {
break;
}
lastCandidate = work;
if (!first) {
first = work;
}
} else if (first && work->fCollapsed) {
*lastPtr = lastCandidate;
return first;
} else {
lastCandidate = nullptr;
SkASSERT(!first);
}
if (work == *lastPtr) {
return first;
}
work = work->fNext;
SkASSERT(work);
} while (true);
if (lastCandidate) {
*lastPtr = lastCandidate;
}
return first;
}
template<typename TCurve, typename OppCurve>
int SkTSect<TCurve, OppCurve>::intersects(SkTSpan<TCurve, OppCurve>* span,
SkTSect<OppCurve, TCurve>* opp,
SkTSpan<OppCurve, TCurve>* oppSpan, int* oppResult) {
bool spanStart, oppStart;
int hullResult = span->hullsIntersect(oppSpan, &spanStart, &oppStart);
if (hullResult >= 0) {
if (hullResult == 2) { // hulls have one point in common
if (!span->fBounded || !span->fBounded->fNext) {
SkASSERT(!span->fBounded || span->fBounded->fBounded == oppSpan);
if (spanStart) {
span->fEndT = span->fStartT;
} else {
span->fStartT = span->fEndT;
}
} else {
hullResult = 1;
}
if (!oppSpan->fBounded || !oppSpan->fBounded->fNext) {
SkASSERT(!oppSpan->fBounded || oppSpan->fBounded->fBounded == span);
if (oppStart) {
oppSpan->fEndT = oppSpan->fStartT;
} else {
oppSpan->fStartT = oppSpan->fEndT;
}
*oppResult = 2;
} else {
*oppResult = 1;
}
} else {
*oppResult = 1;
}
return hullResult;
}
if (span->fIsLine && oppSpan->fIsLine) {
SkIntersections i;
int sects = this->linesIntersect(span, opp, oppSpan, &i);
if (sects == 2) {
return *oppResult = 1;
}
if (!sects) {
return -1;
}
span->fStartT = span->fEndT = i[0][0];
oppSpan->fStartT = oppSpan->fEndT = i[1][0];
return *oppResult = 2;
}
if (span->fIsLinear || oppSpan->fIsLinear) {
return *oppResult = (int) span->linearsIntersect(oppSpan);
}
return *oppResult = 1;
}
template<typename TCurve>
static bool is_parallel(const SkDLine& thisLine, const TCurve& opp) {
if (!opp.IsConic()) {
return false; // FIXME : breaks a lot of stuff now
}
int finds = 0;
SkDLine thisPerp;
thisPerp.fPts[0].fX = thisLine.fPts[1].fX + (thisLine.fPts[1].fY - thisLine.fPts[0].fY);
thisPerp.fPts[0].fY = thisLine.fPts[1].fY + (thisLine.fPts[0].fX - thisLine.fPts[1].fX);
thisPerp.fPts[1] = thisLine.fPts[1];
SkIntersections perpRayI;
perpRayI.intersectRay(opp, thisPerp);
for (int pIndex = 0; pIndex < perpRayI.used(); ++pIndex) {
finds += perpRayI.pt(pIndex).approximatelyEqual(thisPerp.fPts[1]);
}
thisPerp.fPts[1].fX = thisLine.fPts[0].fX + (thisLine.fPts[1].fY - thisLine.fPts[0].fY);
thisPerp.fPts[1].fY = thisLine.fPts[0].fY + (thisLine.fPts[0].fX - thisLine.fPts[1].fX);
thisPerp.fPts[0] = thisLine.fPts[0];
perpRayI.intersectRay(opp, thisPerp);
for (int pIndex = 0; pIndex < perpRayI.used(); ++pIndex) {
finds += perpRayI.pt(pIndex).approximatelyEqual(thisPerp.fPts[0]);
}
return finds >= 2;
}
// while the intersection points are sufficiently far apart:
// construct the tangent lines from the intersections
// find the point where the tangent line intersects the opposite curve
template<typename TCurve, typename OppCurve>
int SkTSect<TCurve, OppCurve>::linesIntersect(SkTSpan<TCurve, OppCurve>* span,
SkTSect<OppCurve, TCurve>* opp,
SkTSpan<OppCurve, TCurve>* oppSpan, SkIntersections* i) {
SkIntersections thisRayI, oppRayI;
SkDLine thisLine = {{ span->fPart[0], span->fPart[TCurve::kPointLast] }};
SkDLine oppLine = {{ oppSpan->fPart[0], oppSpan->fPart[OppCurve::kPointLast] }};
int loopCount = 0;
double bestDistSq = DBL_MAX;
if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
return 0;
}
if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
return 0;
}
// if the ends of each line intersect the opposite curve, the lines are coincident
if (thisRayI.used() > 1) {
int ptMatches = 0;
for (int tIndex = 0; tIndex < thisRayI.used(); ++tIndex) {
for (int lIndex = 0; lIndex < (int) SK_ARRAY_COUNT(thisLine.fPts); ++lIndex) {
ptMatches += thisRayI.pt(tIndex).approximatelyEqual(thisLine.fPts[lIndex]);
}
}
if (ptMatches == 2 || is_parallel(thisLine, opp->fCurve)) {
return 2;
}
}
if (oppRayI.used() > 1) {
int ptMatches = 0;
for (int oIndex = 0; oIndex < oppRayI.used(); ++oIndex) {
for (int lIndex = 0; lIndex < (int) SK_ARRAY_COUNT(thisLine.fPts); ++lIndex) {
ptMatches += oppRayI.pt(oIndex).approximatelyEqual(oppLine.fPts[lIndex]);
}
}
if (ptMatches == 2|| is_parallel(oppLine, this->fCurve)) {
return 2;
}
}
do {
// pick the closest pair of points
double closest = DBL_MAX;
int closeIndex SK_INIT_TO_AVOID_WARNING;
int oppCloseIndex SK_INIT_TO_AVOID_WARNING;
for (int index = 0; index < oppRayI.used(); ++index) {
if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) {
continue;
}
for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) {
if (!roughly_between(oppSpan->fStartT, thisRayI[0][oIndex], oppSpan->fEndT)) {
continue;
}
double distSq = thisRayI.pt(index).distanceSquared(oppRayI.pt(oIndex));
if (closest > distSq) {
closest = distSq;
closeIndex = index;
oppCloseIndex = oIndex;
}
}
}
if (closest == DBL_MAX) {
break;
}
const SkDPoint& oppIPt = thisRayI.pt(oppCloseIndex);
const SkDPoint& iPt = oppRayI.pt(closeIndex);
if (between(span->fStartT, oppRayI[0][closeIndex], span->fEndT)
&& between(oppSpan->fStartT, thisRayI[0][oppCloseIndex], oppSpan->fEndT)
&& oppIPt.approximatelyEqual(iPt)) {
i->merge(oppRayI, closeIndex, thisRayI, oppCloseIndex);
return i->used();
}
double distSq = oppIPt.distanceSquared(iPt);
if (bestDistSq < distSq || ++loopCount > 5) {
return 0;
}
bestDistSq = distSq;
double oppStart = oppRayI[0][closeIndex];
thisLine[0] = fCurve.ptAtT(oppStart);
thisLine[1] = thisLine[0] + fCurve.dxdyAtT(oppStart);
if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
break;
}
double start = thisRayI[0][oppCloseIndex];
oppLine[0] = opp->fCurve.ptAtT(start);
oppLine[1] = oppLine[0] + opp->fCurve.dxdyAtT(start);
if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
break;
}
} while (true);
// convergence may fail if the curves are nearly coincident
SkTCoincident<OppCurve, TCurve> oCoinS, oCoinE;
oCoinS.setPerp(opp->fCurve, oppSpan->fStartT, oppSpan->fPart[0], fCurve);
oCoinE.setPerp(opp->fCurve, oppSpan->fEndT, oppSpan->fPart[OppCurve::kPointLast], fCurve);
double tStart = oCoinS.perpT();
double tEnd = oCoinE.perpT();
bool swap = tStart > tEnd;
if (swap) {
SkTSwap(tStart, tEnd);
}
tStart = SkTMax(tStart, span->fStartT);
tEnd = SkTMin(tEnd, span->fEndT);
if (tStart > tEnd) {
return 0;
}
SkDVector perpS, perpE;
if (tStart == span->fStartT) {
SkTCoincident<TCurve, OppCurve> coinS;
coinS.setPerp(fCurve, span->fStartT, span->fPart[0], opp->fCurve);
perpS = span->fPart[0] - coinS.perpPt();
} else if (swap) {
perpS = oCoinE.perpPt() - oppSpan->fPart[OppCurve::kPointLast];
} else {
perpS = oCoinS.perpPt() - oppSpan->fPart[0];
}
if (tEnd == span->fEndT) {
SkTCoincident<TCurve, OppCurve> coinE;
coinE.setPerp(fCurve, span->fEndT, span->fPart[TCurve::kPointLast], opp->fCurve);
perpE = span->fPart[TCurve::kPointLast] - coinE.perpPt();
} else if (swap) {
perpE = oCoinS.perpPt() - oppSpan->fPart[0];
} else {
perpE = oCoinE.perpPt() - oppSpan->fPart[OppCurve::kPointLast];
}
if (perpS.dot(perpE) >= 0) {
return 0;
}
SkTCoincident<TCurve, OppCurve> coinW;
double workT = tStart;
double tStep = tEnd - tStart;
SkDPoint workPt;
do {
tStep *= 0.5;
if (precisely_zero(tStep)) {
return 0;
}
workT += tStep;
workPt = fCurve.ptAtT(workT);
coinW.setPerp(fCurve, workT, workPt, opp->fCurve);
if (coinW.perpT() < 0) {
continue;
}
SkDVector perpW = workPt - coinW.perpPt();
if ((perpS.dot(perpW) >= 0) == (tStep < 0)) {
tStep = -tStep;
}
if (workPt.approximatelyEqual(coinW.perpPt())) {
break;
}
} while (true);
double oppTTest = coinW.perpT();
if (!opp->fHead->contains(oppTTest)) {
return 0;
}
i->setMax(1);
i->insert(workT, oppTTest, workPt);
return 1;
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::markSpanGone(SkTSpan<TCurve, OppCurve>* span) {
--fActiveCount;
span->fNext = fDeleted;
fDeleted = span;
SkASSERT(!span->fDeleted);
span->fDeleted = true;
}
template<typename TCurve, typename OppCurve>
bool SkTSect<TCurve, OppCurve>::matchedDirection(double t, const SkTSect<OppCurve, TCurve>* sect2,
double t2) const {
SkDVector dxdy = this->fCurve.dxdyAtT(t);
SkDVector dxdy2 = sect2->fCurve.dxdyAtT(t2);
return dxdy.dot(dxdy2) >= 0;
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::matchedDirCheck(double t, const SkTSect<OppCurve, TCurve>* sect2,
double t2, bool* calcMatched, bool* oppMatched) const {
if (*calcMatched) {
SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2));
} else {
*oppMatched = this->matchedDirection(t, sect2, t2);
*calcMatched = true;
}
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::mergeCoincidence(SkTSect<OppCurve, TCurve>* sect2) {
double smallLimit = 0;
do {
// find the smallest unprocessed span
SkTSpan<TCurve, OppCurve>* smaller = nullptr;
SkTSpan<TCurve, OppCurve>* test = fCoincident;
do {
if (test->fStartT < smallLimit) {
continue;
}
if (smaller && smaller->fEndT < test->fStartT) {
continue;
}
smaller = test;
} while ((test = test->fNext));
if (!smaller) {
return;
}
smallLimit = smaller->fEndT;
// find next larger span
SkTSpan<TCurve, OppCurve>* prior = nullptr;
SkTSpan<TCurve, OppCurve>* larger = nullptr;
SkTSpan<TCurve, OppCurve>* largerPrior = nullptr;
test = fCoincident;
do {
if (test->fStartT < smaller->fEndT) {
continue;
}
SkASSERT(test->fStartT != smaller->fEndT);
if (larger && larger->fStartT < test->fStartT) {
continue;
}
largerPrior = prior;
larger = test;
} while ((prior = test), (test = test->fNext));
if (!larger) {
continue;
}
// check middle t value to see if it is coincident as well
double midT = (smaller->fEndT + larger->fStartT) / 2;
SkDPoint midPt = fCurve.ptAtT(midT);
SkTCoincident<TCurve, OppCurve> coin;
coin.setPerp(fCurve, midT, midPt, sect2->fCurve);
if (coin.isCoincident()) {
smaller->fEndT = larger->fEndT;
smaller->fCoinEnd = larger->fCoinEnd;
if (largerPrior) {
largerPrior->fNext = larger->fNext;
} else {
fCoincident = larger->fNext;
}
}
} while (true);
}
template<typename TCurve, typename OppCurve>
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::prev(
SkTSpan<TCurve, OppCurve>* span) const {
SkTSpan<TCurve, OppCurve>* result = nullptr;
SkTSpan<TCurve, OppCurve>* test = fHead;
while (span != test) {
result = test;
test = test->fNext;
SkASSERT(test);
}
return result;
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::recoverCollapsed() {
SkTSpan<TCurve, OppCurve>* deleted = fDeleted;
while (deleted) {
SkTSpan<TCurve, OppCurve>* delNext = deleted->fNext;
if (deleted->fCollapsed) {
SkTSpan<TCurve, OppCurve>** spanPtr = &fHead;
while (*spanPtr && (*spanPtr)->fEndT <= deleted->fStartT) {
spanPtr = &(*spanPtr)->fNext;
}
deleted->fNext = *spanPtr;
*spanPtr = deleted;
}
deleted = delNext;
}
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::removeAllBut(const SkTSpan<OppCurve, TCurve>* keep,
SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp) {
const SkTSpanBounded<OppCurve, TCurve>* testBounded = span->fBounded;
while (testBounded) {
SkTSpan<OppCurve, TCurve>* bounded = testBounded->fBounded;
const SkTSpanBounded<OppCurve, TCurve>* next = testBounded->fNext;
// may have been deleted when opp did 'remove all but'
if (bounded != keep && !bounded->fDeleted) {
SkAssertResult(SkDEBUGCODE(!) span->removeBounded(bounded));
if (bounded->removeBounded(span)) {
opp->removeSpan(bounded);
}
}
testBounded = next;
}
SkASSERT(!span->fDeleted);
SkASSERT(span->findOppSpan(keep));
SkASSERT(keep->findOppSpan(span));
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::removeByPerpendicular(SkTSect<OppCurve, TCurve>* opp) {
SkTSpan<TCurve, OppCurve>* test = fHead;
SkTSpan<TCurve, OppCurve>* next;
do {
next = test->fNext;
if (test->fCoinStart.perpT() < 0 || test->fCoinEnd.perpT() < 0) {
continue;
}
SkDVector startV = test->fCoinStart.perpPt() - test->fPart[0];
SkDVector endV = test->fCoinEnd.perpPt() - test->fPart[TCurve::kPointLast];
#if DEBUG_T_SECT
SkDebugf("%s startV=(%1.9g,%1.9g) endV=(%1.9g,%1.9g) dot=%1.9g\n", __FUNCTION__,
startV.fX, startV.fY, endV.fX, endV.fY, startV.dot(endV));
#endif
if (startV.dot(endV) <= 0) {
continue;
}
this->removeSpans(test, opp);
} while ((test = next));
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::removeCoincident(SkTSpan<TCurve, OppCurve>* span, bool isBetween) {
this->unlinkSpan(span);
if (isBetween || between(0, span->fCoinStart.perpT(), 1)) {
--fActiveCount;
span->fNext = fCoincident;
fCoincident = span;
} else {
this->markSpanGone(span);
}
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::removeSpan(SkTSpan<TCurve, OppCurve>* span) {
this->unlinkSpan(span);
this->markSpanGone(span);
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::removeSpanRange(SkTSpan<TCurve, OppCurve>* first,
SkTSpan<TCurve, OppCurve>* last) {
if (first == last) {
return;
}
SkTSpan<TCurve, OppCurve>* span = first;
SkASSERT(span);
SkTSpan<TCurve, OppCurve>* final = last->fNext;
SkTSpan<TCurve, OppCurve>* next = span->fNext;
while ((span = next) && span != final) {
next = span->fNext;
this->markSpanGone(span);
}
if (final) {
final->fPrev = first;
}
first->fNext = final;
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::removeSpans(SkTSpan<TCurve, OppCurve>* span,
SkTSect<OppCurve, TCurve>* opp) {
SkTSpanBounded<OppCurve, TCurve>* bounded = span->fBounded;
while (bounded) {
SkTSpan<OppCurve, TCurve>* spanBounded = bounded->fBounded;
SkTSpanBounded<OppCurve, TCurve>* next = bounded->fNext;
if (span->removeBounded(spanBounded)) { // shuffles last into position 0
this->removeSpan(span);
}
if (spanBounded->removeBounded(span)) {
opp->removeSpan(spanBounded);
}
SkASSERT(!span->fDeleted || !opp->debugHasBounded(span));
bounded = next;
}
}
template<typename TCurve, typename OppCurve>
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::spanAtT(double t,
SkTSpan<TCurve, OppCurve>** priorSpan) {
SkTSpan<TCurve, OppCurve>* test = fHead;
SkTSpan<TCurve, OppCurve>* prev = nullptr;
while (test && test->fEndT < t) {
prev = test;
test = test->fNext;
}
*priorSpan = prev;
return test && test->fStartT <= t ? test : nullptr;
}
template<typename TCurve, typename OppCurve>
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::tail() {
SkTSpan<TCurve, OppCurve>* result = fHead;
SkTSpan<TCurve, OppCurve>* 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, typename OppCurve>
void SkTSect<TCurve, OppCurve>::trim(SkTSpan<TCurve, OppCurve>* span,
SkTSect<OppCurve, TCurve>* opp) {
span->initBounds(fCurve);
const SkTSpanBounded<OppCurve, TCurve>* testBounded = span->fBounded;
while (testBounded) {
SkTSpan<OppCurve, TCurve>* test = testBounded->fBounded;
const SkTSpanBounded<OppCurve, TCurve>* next = testBounded->fNext;
int oppSects, sects = this->intersects(span, opp, test, &oppSects);
if (sects >= 1) {
if (oppSects == 2) {
test->initBounds(opp->fCurve);
opp->removeAllBut(span, test, this);
}
if (sects == 2) {
span->initBounds(fCurve);
this->removeAllBut(test, span, opp);
return;
}
} else {
if (span->removeBounded(test)) {
this->removeSpan(span);
}
if (test->removeBounded(span)) {
opp->removeSpan(test);
}
}
testBounded = next;
}
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::unlinkSpan(SkTSpan<TCurve, OppCurve>* span) {
SkTSpan<TCurve, OppCurve>* prev = span->fPrev;
SkTSpan<TCurve, OppCurve>* next = span->fNext;
if (prev) {
prev->fNext = next;
if (next) {
next->fPrev = prev;
}
} else {
fHead = next;
if (next) {
next->fPrev = nullptr;
}
}
}
template<typename TCurve, typename OppCurve>
bool SkTSect<TCurve, OppCurve>::updateBounded(SkTSpan<TCurve, OppCurve>* first,
SkTSpan<TCurve, OppCurve>* last, SkTSpan<OppCurve, TCurve>* oppFirst) {
SkTSpan<TCurve, OppCurve>* test = first;
const SkTSpan<TCurve, OppCurve>* final = last->next();
bool deleteSpan = false;
do {
deleteSpan |= test->removeAllBounded();
} while ((test = test->fNext) != final);
first->fBounded = nullptr;
first->addBounded(oppFirst, &fHeap);
// cannot call validate until remove span range is called
return deleteSpan;
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::validate() const {
#if DEBUG_T_SECT
int count = 0;
if (fHead) {
const SkTSpan<TCurve, OppCurve>* span = fHead;
SkASSERT(!span->fPrev);
SkDEBUGCODE(double last = 0);
do {
span->validate();
SkASSERT(span->fStartT >= last);
SkDEBUGCODE(last = span->fEndT);
++count;
} while ((span = span->fNext) != nullptr);
}
SkASSERT(count == fActiveCount);
SkASSERT(fActiveCount <= fDebugAllocatedCount);
int deletedCount = 0;
const SkTSpan<TCurve, OppCurve>* deleted = fDeleted;
while (deleted) {
++deletedCount;
deleted = deleted->fNext;
}
const SkTSpan<TCurve, OppCurve>* coincident = fCoincident;
while (coincident) {
++deletedCount;
coincident = coincident->fNext;
}
SkASSERT(fActiveCount + deletedCount == fDebugAllocatedCount);
#endif
}
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::validateBounded() const {
#if DEBUG_T_SECT
if (!fHead) {
return;
}
const SkTSpan<TCurve, OppCurve>* span = fHead;
do {
span->validateBounded();
} while ((span = span->fNext) != nullptr);
#endif
}
template<typename TCurve, typename OppCurve>
int SkTSect<TCurve, OppCurve>::EndsEqual(const SkTSect<TCurve, OppCurve>* sect1,
const SkTSect<OppCurve, TCurve>* sect2, SkIntersections* intersections) {
int zeroOneSet = 0;
if (sect1->fCurve[0] == sect2->fCurve[0]) {
zeroOneSet |= kZeroS1Set | kZeroS2Set;
intersections->insert(0, 0, sect1->fCurve[0]);
}
if (sect1->fCurve[0] == sect2->fCurve[OppCurve::kPointLast]) {
zeroOneSet |= kZeroS1Set | kOneS2Set;
intersections->insert(0, 1, sect1->fCurve[0]);
}
if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[0]) {
zeroOneSet |= kOneS1Set | kZeroS2Set;
intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
}
if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[OppCurve::kPointLast]) {
zeroOneSet |= kOneS1Set | kOneS2Set;
intersections->insert(1, 1, sect1->fCurve[TCurve::kPointLast]);
}
// check for zero
if (!(zeroOneSet & (kZeroS1Set | kZeroS2Set))
&& sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
zeroOneSet |= kZeroS1Set | kZeroS2Set;
intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
}
if (!(zeroOneSet & (kZeroS1Set | kOneS2Set))
&& sect1->fCurve[0].approximatelyEqual(sect2->fCurve[OppCurve::kPointLast])) {
zeroOneSet |= kZeroS1Set | kOneS2Set;
intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[OppCurve::kPointLast]);
}
// check for one
if (!(zeroOneSet & (kOneS1Set | kZeroS2Set))
&& sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
zeroOneSet |= kOneS1Set | kZeroS2Set;
intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
}
if (!(zeroOneSet & (kOneS1Set | kOneS2Set))
&& sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[
OppCurve::kPointLast])) {
zeroOneSet |= kOneS1Set | kOneS2Set;
intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
sect2->fCurve[OppCurve::kPointLast]);
}
return zeroOneSet;
}
template<typename TCurve, typename OppCurve>
struct SkClosestRecord {
bool operator<(const SkClosestRecord& rh) const {
return fClosest < rh.fClosest;
}
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, OppCurve>* span1, const SkTSpan<OppCurve, TCurve>* span2,
int c1Index, int c2Index) {
const TCurve& c1 = span1->part();
const OppCurve& 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 = nullptr);
SkDEBUGCODE(fC2Span = nullptr);
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, OppCurve>* fC1Span;
const SkTSpan<OppCurve, TCurve>* fC2Span;
double fC1StartT;
double fC1EndT;
double fC2StartT;
double fC2EndT;
double fClosest;
int fC1Index;
int fC2Index;
};
template<typename TCurve, typename OppCurve>
struct SkClosestSect {
SkClosestSect()
: fUsed(0) {
fClosest.push_back().reset();
}
bool find(const SkTSpan<TCurve, OppCurve>* span1, const SkTSpan<OppCurve, TCurve>* span2) {
SkClosestRecord<TCurve, OppCurve>* record = &fClosest[fUsed];
record->findEnd(span1, span2, 0, 0);
record->findEnd(span1, span2, 0, OppCurve::kPointLast);
record->findEnd(span1, span2, TCurve::kPointLast, 0);
record->findEnd(span1, span2, TCurve::kPointLast, OppCurve::kPointLast);
if (record->fClosest == FLT_MAX) {
return false;
}
for (int index = 0; index < fUsed; ++index) {
SkClosestRecord<TCurve, OppCurve>* test = &fClosest[index];
if (test->matesWith(*record)) {
if (test->fClosest > record->fClosest) {
test->merge(*record);
}
test->update(*record);
record->reset();
return false;
}
}
++fUsed;
fClosest.push_back().reset();
return true;
}
void finish(SkIntersections* intersections) const {
SkSTArray<TCurve::kMaxIntersections * 3,
const SkClosestRecord<TCurve, OppCurve>*, true> closestPtrs;
for (int index = 0; index < fUsed; ++index) {
closestPtrs.push_back(&fClosest[index]);
}
SkTQSort<const SkClosestRecord<TCurve, OppCurve> >(closestPtrs.begin(), closestPtrs.end()
- 1);
for (int index = 0; index < fUsed; ++index) {
const SkClosestRecord<TCurve, OppCurve>* test = closestPtrs[index];
test->addIntersection(intersections);
}
}
// this is oversized so that an extra records can merge into final one
SkSTArray<TCurve::kMaxIntersections * 2, SkClosestRecord<TCurve, OppCurve>, true> fClosest;
int fUsed;
};
// returns true if the rect is too small to consider
template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::BinarySearch(SkTSect<TCurve, OppCurve>* sect1,
SkTSect<OppCurve, TCurve>* sect2, SkIntersections* intersections) {
#if DEBUG_T_SECT_DUMP > 1
gDumpTSectNum = 0;
#endif
SkDEBUGCODE(sect1->fOppSect = sect2);
SkDEBUGCODE(sect2->fOppSect = sect1);
intersections->reset();
intersections->setMax(TCurve::kMaxIntersections + 3); // give extra for slop
SkTSpan<TCurve, OppCurve>* span1 = sect1->fHead;
SkTSpan<OppCurve, TCurve>* span2 = sect2->fHead;
int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect);
// SkASSERT(between(0, sect, 2));
if (!sect) {
return;
}
if (sect == 2 && oppSect == 2) {
(void) EndsEqual(sect1, sect2, intersections);
return;
}
span1->addBounded(span2, &sect1->fHeap);
span2->addBounded(span1, &sect2->fHeap);
const int kMaxCoinLoopCount = 8;
int coinLoopCount = kMaxCoinLoopCount;
double start1s SK_INIT_TO_AVOID_WARNING;
double start1e SK_INIT_TO_AVOID_WARNING;
do {
// find the largest bounds
SkTSpan<TCurve, OppCurve>* largest1 = sect1->boundsMax();
if (!largest1) {
break;
}
SkTSpan<OppCurve, TCurve>* largest2 = sect2->boundsMax();
// split it
if (!largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax
|| (!largest1->fCollapsed && largest2->fCollapsed)))) {
if (largest1->fCollapsed) {
break;
}
// trim parts that don't intersect the opposite
SkTSpan<TCurve, OppCurve>* half1 = sect1->addOne();
if (!half1->split(largest1, &sect1->fHeap)) {
break;
}
sect1->trim(largest1, sect2);
sect1->trim(half1, sect2);
} else {
if (largest2->fCollapsed) {
break;
}
// trim parts that don't intersect the opposite
SkTSpan<OppCurve, TCurve>* half2 = sect2->addOne();
if (!half2->split(largest2, &sect2->fHeap)) {
break;
}
sect2->trim(largest2, sect1);
sect2->trim(half2, sect1);
}
sect1->validate();
sect2->validate();
#if DEBUG_T_SECT_LOOP_COUNT
intersections->debugBumpLoopCount(SkIntersections::kIterations_DebugLoop);
#endif
// if there are 9 or more continuous spans on both sects, suspect coincidence
if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
&& sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
if (coinLoopCount == kMaxCoinLoopCount) {
start1s = sect1->fHead->fStartT;
start1e = sect1->tail()->fEndT;
}
sect1->coincidentCheck(sect2);
sect1->validate();
sect2->validate();
#if DEBUG_T_SECT_LOOP_COUNT
intersections->debugBumpLoopCount(SkIntersections::kCoinCheck_DebugLoop);
#endif
if (!--coinLoopCount && sect1->fHead && sect2->fHead) {
/* All known working cases resolve in two tries. Sadly, cubicConicTests[0]
gets stuck in a loop. It adds an extension to allow a coincident end
perpendicular to track its intersection in the opposite curve. However,
the bounding box of the extension does not intersect the original curve,
so the extension is discarded, only to be added again the next time around. */
sect1->coincidentForce(sect2, start1s, start1e);
sect1->validate();
sect2->validate();
}
}
if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
&& sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
sect1->computePerpendiculars(sect2, sect1->fHead, sect1->tail());
sect2->computePerpendiculars(sect1, sect2->fHead, sect2->tail());
sect1->removeByPerpendicular(sect2);
sect1->validate();
sect2->validate();
#if DEBUG_T_SECT_LOOP_COUNT
intersections->debugBumpLoopCount(SkIntersections::kComputePerp_DebugLoop);
#endif
if (sect1->collapsed() > TCurve::kMaxIntersections) {
break;
}
}
#if DEBUG_T_SECT_DUMP
sect1->dumpBoth(sect2);
#endif
if (!sect1->fHead || !sect2->fHead) {
break;
}
} while (true);
SkTSpan<TCurve, OppCurve>* coincident = sect1->fCoincident;
if (coincident) {
// if there is more than one coincident span, check loosely to see if they should be joined
if (coincident->fNext) {
sect1->mergeCoincidence(sect2);
coincident = sect1->fCoincident;
}
SkASSERT(sect2->fCoincident); // courtesy check : coincidence only looks at sect 1
do {
if (!coincident->fCoinStart.isCoincident()) {
continue;
}
if (!coincident->fCoinEnd.isCoincident()) {
continue;
}
int index = intersections->insertCoincident(coincident->fStartT,
coincident->fCoinStart.perpT(), coincident->fPart[0]);
if ((intersections->insertCoincident(coincident->fEndT,
coincident->fCoinEnd.perpT(),
coincident->fPart[TCurve::kPointLast]) < 0) && index >= 0) {
intersections->clearCoincidence(index);
}
} while ((coincident = coincident->fNext));
}
int zeroOneSet = EndsEqual(sect1, sect2, intersections);
if (!sect1->fHead || !sect2->fHead) {
return;
}
sect1->recoverCollapsed();
sect2->recoverCollapsed();
SkTSpan<TCurve, OppCurve>* result1 = sect1->fHead;
// check heads and tails for zero and ones and insert them if we haven't already done so
const SkTSpan<TCurve, OppCurve>* head1 = result1;
if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) {
const SkDPoint& start1 = sect1->fCurve[0];
if (head1->isBounded()) {
double t = head1->closestBoundedT(start1);
if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
intersections->insert(0, t, start1);
}
}
}
const SkTSpan<OppCurve, TCurve>* head2 = sect2->fHead;
if (!(zeroOneSet & kZeroS2Set) && approximately_less_than_zero(head2->fStartT)) {
const SkDPoint& start2 = sect2->fCurve[0];
if (head2->isBounded()) {
double t = head2->closestBoundedT(start2);
if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
intersections->insert(t, 0, start2);
}
}
}
const SkTSpan<TCurve, OppCurve>* tail1 = sect1->tail();
if (!(zeroOneSet & kOneS1Set) && approximately_greater_than_one(tail1->fEndT)) {
const SkDPoint& end1 = sect1->fCurve[TCurve::kPointLast];
if (tail1->isBounded()) {
double t = tail1->closestBoundedT(end1);
if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
intersections->insert(1, t, end1);
}
}
}
const SkTSpan<OppCurve, TCurve>* tail2 = sect2->tail();
if (!(zeroOneSet & kOneS2Set) && approximately_greater_than_one(tail2->fEndT)) {
const SkDPoint& end2 = sect2->fCurve[OppCurve::kPointLast];
if (tail2->isBounded()) {
double t = tail2->closestBoundedT(end2);
if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
intersections->insert(t, 1, end2);
}
}
}
SkClosestSect<TCurve, OppCurve> closest;
do {
while (result1 && result1->fCoinStart.isCoincident() && result1->fCoinEnd.isCoincident()) {
result1 = result1->fNext;
}
if (!result1) {
break;
}
SkTSpan<OppCurve, TCurve>* result2 = sect2->fHead;
bool found = false;
while (result2) {
found |= closest.find(result1, result2);
result2 = result2->fNext;
}
} while ((result1 = result1->fNext));
closest.finish(intersections);
// if there is more than one intersection and it isn't already coincident, check
int last = intersections->used() - 1;
for (int index = 0; index < last; ) {
if (intersections->isCoincident(index) && intersections->isCoincident(index + 1)) {
++index;
continue;
}
double midT = ((*intersections)[0][index] + (*intersections)[0][index + 1]) / 2;
SkDPoint midPt = sect1->fCurve.ptAtT(midT);
// intersect perpendicular with opposite curve
SkTCoincident<TCurve, OppCurve> perp;
perp.setPerp(sect1->fCurve, midT, midPt, sect2->fCurve);
if (!perp.isCoincident()) {
++index;
continue;
}
if (intersections->isCoincident(index)) {
intersections->removeOne(index);
--last;
} else if (intersections->isCoincident(index + 1)) {
intersections->removeOne(index + 1);
--last;
} else {
intersections->setCoincident(index++);
}
intersections->setCoincident(index);
}
SkASSERT(intersections->used() <= TCurve::kMaxIntersections);
}