new files for pathops geometric intersection
There's no gyp references to these new files, so this should only have the effect of reducing the size of the commit that turns this code on. TBR= Review URL: https://codereview.chromium.org/853223002
This commit is contained in:
parent
40248f365b
commit
45fa447460
86
src/pathops/SkOpBuilder.cpp
Normal file
86
src/pathops/SkOpBuilder.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 "SkMatrix.h"
|
||||
#include "SkPath.h"
|
||||
#include "SkPathOps.h"
|
||||
|
||||
void SkOpBuilder::add(const SkPath& path, SkPathOp op) {
|
||||
if (0 == fOps.count() && op != kUnion_PathOp) {
|
||||
fPathRefs.push_back() = SkPath();
|
||||
*fOps.append() = kUnion_PathOp;
|
||||
}
|
||||
fPathRefs.push_back() = path;
|
||||
*fOps.append() = op;
|
||||
}
|
||||
|
||||
void SkOpBuilder::reset() {
|
||||
fPathRefs.reset();
|
||||
fOps.reset();
|
||||
}
|
||||
|
||||
/* OPTIMIZATION: Union doesn't need to be all-or-nothing. A run of three or more convex
|
||||
paths with union ops could be locally resolved and still improve over doing the
|
||||
ops one at a time. */
|
||||
bool SkOpBuilder::resolve(SkPath* result) {
|
||||
int count = fOps.count();
|
||||
bool allUnion = true;
|
||||
SkPath::Direction firstDir;
|
||||
for (int index = 0; index < count; ++index) {
|
||||
SkPath* test = &fPathRefs[index];
|
||||
if (kUnion_PathOp != fOps[index] || test->isInverseFillType()) {
|
||||
allUnion = false;
|
||||
break;
|
||||
}
|
||||
// If all paths are convex, track direction, reversing as needed.
|
||||
if (test->isConvex()) {
|
||||
SkPath::Direction dir;
|
||||
if (!test->cheapComputeDirection(&dir)) {
|
||||
allUnion = false;
|
||||
break;
|
||||
}
|
||||
if (index == 0) {
|
||||
firstDir = dir;
|
||||
} else if (firstDir != dir) {
|
||||
SkPath temp;
|
||||
temp.reverseAddPath(*test);
|
||||
*test = temp;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// If the path is not convex but its bounds do not intersect the others, simplify is enough.
|
||||
const SkRect& testBounds = test->getBounds();
|
||||
for (int inner = 0; inner < index; ++inner) {
|
||||
// OPTIMIZE: check to see if the contour bounds do not intersect other contour bounds?
|
||||
if (SkRect::Intersects(fPathRefs[inner].getBounds(), testBounds)) {
|
||||
allUnion = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!allUnion) {
|
||||
*result = fPathRefs[0];
|
||||
for (int index = 1; index < count; ++index) {
|
||||
if (!Op(*result, fPathRefs[index], fOps[index], result)) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
SkPath sum;
|
||||
for (int index = 0; index < count; ++index) {
|
||||
if (!Simplify(fPathRefs[index], &fPathRefs[index])) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
sum.addPath(fPathRefs[index]);
|
||||
}
|
||||
reset();
|
||||
return Simplify(sum, result);
|
||||
}
|
41
src/pathops/SkOpCoincidence.h
Normal file
41
src/pathops/SkOpCoincidence.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkOpCoincidence_DEFINED
|
||||
#define SkOpCoincidence_DEFINED
|
||||
|
||||
#include "SkOpTAllocator.h"
|
||||
#include "SkOpSpan.h"
|
||||
|
||||
class SkOpPtT;
|
||||
|
||||
struct SkCoincidentSpans {
|
||||
SkCoincidentSpans* fNext;
|
||||
SkOpPtT* fCoinPtTStart;
|
||||
SkOpPtT* fCoinPtTEnd;
|
||||
SkOpPtT* fOppPtTStart;
|
||||
SkOpPtT* fOppPtTEnd;
|
||||
bool fFlipped;
|
||||
};
|
||||
|
||||
class SkOpCoincidence {
|
||||
public:
|
||||
SkOpCoincidence()
|
||||
: fHead(NULL) {
|
||||
}
|
||||
|
||||
void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
|
||||
SkOpPtT* oppPtTEnd, bool flipped, SkChunkAlloc* allocator);
|
||||
void apply();
|
||||
bool contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
|
||||
SkOpPtT* oppPtTEnd, bool flipped);
|
||||
void dump() const;
|
||||
void mark();
|
||||
|
||||
SkCoincidentSpans* fHead;
|
||||
};
|
||||
|
||||
#endif
|
126
src/pathops/SkOpCubicHull.cpp
Normal file
126
src/pathops/SkOpCubicHull.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "SkPathOpsCubic.h"
|
||||
|
||||
static bool rotate(const SkDCubic& cubic, int zero, int index, SkDCubic& rotPath) {
|
||||
double dy = cubic[index].fY - cubic[zero].fY;
|
||||
double dx = cubic[index].fX - cubic[zero].fX;
|
||||
if (approximately_zero(dy)) {
|
||||
if (approximately_zero(dx)) {
|
||||
return false;
|
||||
}
|
||||
rotPath = cubic;
|
||||
return true;
|
||||
}
|
||||
for (int index = 0; index < 4; ++index) {
|
||||
rotPath[index].fX = cubic[index].fX * dx + cubic[index].fY * dy;
|
||||
rotPath[index].fY = cubic[index].fY * dx - cubic[index].fX * dy;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Returns 0 if negative, 1 if zero, 2 if positive
|
||||
static int side(double x) {
|
||||
return (x > 0) + (x >= 0);
|
||||
}
|
||||
|
||||
/* Given a cubic, find the convex hull described by the end and control points.
|
||||
The hull may have 3 or 4 points. Cubics that degenerate into a point or line
|
||||
are not considered.
|
||||
|
||||
The hull is computed by assuming that three points, if unique and non-linear,
|
||||
form a triangle. The fourth point may replace one of the first three, may be
|
||||
discarded if in the triangle or on an edge, or may be inserted between any of
|
||||
the three to form a convex quadralateral.
|
||||
|
||||
The indices returned in order describe the convex hull.
|
||||
*/
|
||||
int SkDCubic::convexHull(char order[4]) const {
|
||||
size_t index;
|
||||
// find top point
|
||||
size_t yMin = 0;
|
||||
for (index = 1; index < 4; ++index) {
|
||||
if (fPts[yMin].fY > fPts[index].fY || (fPts[yMin].fY == fPts[index].fY
|
||||
&& fPts[yMin].fX > fPts[index].fX)) {
|
||||
yMin = index;
|
||||
}
|
||||
}
|
||||
order[0] = yMin;
|
||||
int midX = -1;
|
||||
int backupYMin = -1;
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
for (index = 0; index < 4; ++index) {
|
||||
if (index == yMin) {
|
||||
continue;
|
||||
}
|
||||
// rotate line from (yMin, index) to axis
|
||||
// see if remaining two points are both above or below
|
||||
// use this to find mid
|
||||
int mask = other_two(yMin, index);
|
||||
int side1 = yMin ^ mask;
|
||||
int side2 = index ^ mask;
|
||||
SkDCubic rotPath;
|
||||
if (!rotate(*this, yMin, index, rotPath)) { // ! if cbc[yMin]==cbc[idx]
|
||||
order[1] = side1;
|
||||
order[2] = side2;
|
||||
return 3;
|
||||
}
|
||||
int sides = side(rotPath[side1].fY - rotPath[yMin].fY);
|
||||
sides ^= side(rotPath[side2].fY - rotPath[yMin].fY);
|
||||
if (sides == 2) { // '2' means one remaining point <0, one >0
|
||||
if (midX >= 0) {
|
||||
// one of the control points is equal to an end point
|
||||
order[0] = 0;
|
||||
order[1] = 3;
|
||||
if (fPts[1] == fPts[0] || fPts[1] == fPts[3]) {
|
||||
order[2] = 2;
|
||||
return 3;
|
||||
}
|
||||
SkASSERT(fPts[2] == fPts[0] || fPts[2] == fPts[3]);
|
||||
order[2] = 1;
|
||||
return 3;
|
||||
}
|
||||
midX = index;
|
||||
} else if (sides == 0) { // '0' means both to one side or the other
|
||||
backupYMin = index;
|
||||
}
|
||||
}
|
||||
if (midX >= 0) {
|
||||
break;
|
||||
}
|
||||
if (backupYMin < 0) {
|
||||
break;
|
||||
}
|
||||
yMin = backupYMin;
|
||||
backupYMin = -1;
|
||||
}
|
||||
if (midX < 0) {
|
||||
midX = yMin ^ 3; // choose any other point
|
||||
}
|
||||
int mask = other_two(yMin, midX);
|
||||
int least = yMin ^ mask;
|
||||
int most = midX ^ mask;
|
||||
order[0] = yMin;
|
||||
order[1] = least;
|
||||
|
||||
// see if mid value is on same side of line (least, most) as yMin
|
||||
SkDCubic midPath;
|
||||
if (!rotate(*this, least, most, midPath)) { // ! if cbc[least]==cbc[most]
|
||||
order[2] = midX;
|
||||
return 3;
|
||||
}
|
||||
int midSides = side(midPath[yMin].fY - midPath[least].fY);
|
||||
midSides ^= side(midPath[midX].fY - midPath[least].fY);
|
||||
if (midSides != 2) { // if mid point is not between
|
||||
order[2] = most;
|
||||
return 3; // result is a triangle
|
||||
}
|
||||
order[2] = midX;
|
||||
order[3] = most;
|
||||
return 4; // result is a quadralateral
|
||||
}
|
27
src/pathops/SkOpTAllocator.h
Normal file
27
src/pathops/SkOpTAllocator.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkOpTAllocator_DEFINED
|
||||
#define SkOpTAllocator_DEFINED
|
||||
|
||||
#include "SkChunkAlloc.h"
|
||||
|
||||
// T is SkOpAngle2, SkOpSpan2, or SkOpSegment2
|
||||
template<typename T>
|
||||
class SkOpTAllocator {
|
||||
public:
|
||||
static T* Allocate(SkChunkAlloc* allocator) {
|
||||
void* ptr = allocator->allocThrow(sizeof(T));
|
||||
T* record = (T*) ptr;
|
||||
return record;
|
||||
}
|
||||
|
||||
static T* New(SkChunkAlloc* allocator) {
|
||||
return new (Allocate(allocator)) T();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
175
src/pathops/SkPathOpsCubicSect.h
Normal file
175
src/pathops/SkPathOpsCubicSect.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkCubicSpan_DEFINE
|
||||
#define SkCubicSpan_DEFINE
|
||||
|
||||
#include "SkChunkAlloc.h"
|
||||
#include "SkPathOpsRect.h"
|
||||
#include "SkPathOpsCubic.h"
|
||||
#include "SkTArray.h"
|
||||
|
||||
class SkIntersections;
|
||||
|
||||
class SkCubicCoincident {
|
||||
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 SkDCubic& cubic1, double t, const SkDPoint& qPt, const SkDCubic& cubic2);
|
||||
|
||||
private:
|
||||
SkDPoint fPerpPt;
|
||||
double fPerpT; // perpendicular intersection on opposite Cubic
|
||||
bool fCoincident;
|
||||
};
|
||||
|
||||
class SkCubicSect; // used only by debug id
|
||||
|
||||
class SkCubicSpan {
|
||||
public:
|
||||
void init(const SkDCubic& Cubic);
|
||||
void initBounds(const SkDCubic& Cubic);
|
||||
|
||||
bool contains(double t) const {
|
||||
return !! const_cast<SkCubicSpan*>(this)->innerFind(t);
|
||||
}
|
||||
|
||||
bool contains(const SkCubicSpan* span) const;
|
||||
|
||||
SkCubicSpan* find(double t) {
|
||||
SkCubicSpan* result = innerFind(t);
|
||||
SkASSERT(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool intersects(const SkCubicSpan* span) const;
|
||||
|
||||
const SkCubicSpan* next() const {
|
||||
return fNext;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
fBounded.reset();
|
||||
}
|
||||
|
||||
bool split(SkCubicSpan* work) {
|
||||
return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
|
||||
}
|
||||
|
||||
bool splitAt(SkCubicSpan* work, double t);
|
||||
bool tightBoundsIntersects(const SkCubicSpan* span) const;
|
||||
|
||||
// implementation is for testing only
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
bool hullIntersects(const SkDCubic& ) const;
|
||||
SkCubicSpan* innerFind(double t);
|
||||
bool linearIntersects(const SkDCubic& ) const;
|
||||
|
||||
// implementation is for testing only
|
||||
#if DEBUG_BINARY_CUBIC
|
||||
int debugID(const SkCubicSect* ) const { return fDebugID; }
|
||||
#else
|
||||
int debugID(const SkCubicSect* ) const;
|
||||
#endif
|
||||
void dump(const SkCubicSect* ) const;
|
||||
void dumpID(const SkCubicSect* ) const;
|
||||
|
||||
#if DEBUG_BINARY_CUBIC
|
||||
void validate() const;
|
||||
#endif
|
||||
|
||||
SkDCubic fPart;
|
||||
SkCubicCoincident fCoinStart;
|
||||
SkCubicCoincident fCoinEnd;
|
||||
SkSTArray<4, SkCubicSpan*, true> fBounded;
|
||||
SkCubicSpan* fPrev;
|
||||
SkCubicSpan* fNext;
|
||||
SkDRect fBounds;
|
||||
double fStartT;
|
||||
double fEndT;
|
||||
double fBoundsMax;
|
||||
bool fCollapsed;
|
||||
bool fHasPerp;
|
||||
mutable bool fIsLinear;
|
||||
#if DEBUG_BINARY_CUBIC
|
||||
int fDebugID;
|
||||
bool fDebugDeleted;
|
||||
#endif
|
||||
friend class SkCubicSect;
|
||||
};
|
||||
|
||||
class SkCubicSect {
|
||||
public:
|
||||
SkCubicSect(const SkDCubic& Cubic PATH_OPS_DEBUG_PARAMS(int id));
|
||||
static void BinarySearch(SkCubicSect* sect1, SkCubicSect* sect2, SkIntersections* intersections);
|
||||
|
||||
// for testing only
|
||||
void dumpCubics() const;
|
||||
private:
|
||||
SkCubicSpan* addOne();
|
||||
bool binarySearchCoin(const SkCubicSect& , double tStart, double tStep, double* t,
|
||||
double* oppT);
|
||||
SkCubicSpan* boundsMax() const;
|
||||
void coincidentCheck(SkCubicSect* sect2);
|
||||
bool intersects(const SkCubicSpan* span, const SkCubicSect* opp, const SkCubicSpan* oppSpan) const;
|
||||
void onCurveCheck(SkCubicSect* sect2, SkCubicSpan* first, SkCubicSpan* last);
|
||||
void recoverCollapsed();
|
||||
void removeSpan(SkCubicSpan* span);
|
||||
void removeOne(const SkCubicSpan* test, SkCubicSpan* span);
|
||||
void removeSpans(SkCubicSpan* span, SkCubicSect* opp);
|
||||
void setPerp(const SkDCubic& opp, SkCubicSpan* first, SkCubicSpan* last);
|
||||
void trim(SkCubicSpan* span, SkCubicSect* opp);
|
||||
|
||||
// for testing only
|
||||
void dump() const;
|
||||
void dumpBoth(const SkCubicSect& opp) const;
|
||||
void dumpBoth(const SkCubicSect* opp) const;
|
||||
|
||||
#if DEBUG_BINARY_CUBIC
|
||||
int debugID() const { return fDebugID; }
|
||||
void validate() const;
|
||||
#else
|
||||
int debugID() const { return 0; }
|
||||
#endif
|
||||
const SkDCubic& fCubic;
|
||||
SkChunkAlloc fHeap;
|
||||
SkCubicSpan* fHead;
|
||||
SkCubicSpan* fDeleted;
|
||||
int fActiveCount;
|
||||
#if DEBUG_BINARY_CUBIC
|
||||
int fDebugID;
|
||||
int fDebugCount;
|
||||
int fDebugAllocatedCount;
|
||||
#endif
|
||||
friend class SkCubicSpan; // only used by debug id
|
||||
};
|
||||
|
||||
#endif
|
502
src/pathops/SkPathOpsPostSect.cpp
Normal file
502
src/pathops/SkPathOpsPostSect.cpp
Normal file
@ -0,0 +1,502 @@
|
||||
/*
|
||||
* 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 "SkOpCoincidence.h"
|
||||
#include "SkOpContour.h"
|
||||
#include "SkOpSegment.h"
|
||||
#include "SkPathWriter.h"
|
||||
|
||||
bool SkOpPtT::alias() const {
|
||||
return this->span()->ptT() != this;
|
||||
}
|
||||
|
||||
SkOpContour* SkOpPtT::contour() const {
|
||||
return segment()->contour();
|
||||
}
|
||||
|
||||
SkOpDebugState* SkOpPtT::debugState() const {
|
||||
return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL);
|
||||
}
|
||||
|
||||
void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
|
||||
fT = t;
|
||||
fPt = pt;
|
||||
fSpan = span;
|
||||
fNext = this;
|
||||
fDuplicatePt = duplicate;
|
||||
fDeleted = false;
|
||||
PATH_OPS_DEBUG_CODE(fID = ++span->debugState()->fPtTID);
|
||||
}
|
||||
|
||||
bool SkOpPtT::onEnd() const {
|
||||
const SkOpSpanBase* span = this->span();
|
||||
if (span->ptT() != this) {
|
||||
return false;
|
||||
}
|
||||
const SkOpSegment* segment = this->segment();
|
||||
return span == segment->head() || span == segment->tail();
|
||||
}
|
||||
|
||||
SkOpPtT* SkOpPtT::remove() {
|
||||
SkOpPtT* prev = this;
|
||||
do {
|
||||
SkOpPtT* next = prev->fNext;
|
||||
if (next == this) {
|
||||
prev->removeNext();
|
||||
fDeleted = true;
|
||||
return prev;
|
||||
}
|
||||
prev = next;
|
||||
} while (prev != this);
|
||||
SkASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SkOpPtT::removeNext() {
|
||||
SkASSERT(this->fNext);
|
||||
SkOpPtT* next = this->fNext;
|
||||
this->fNext = next->fNext;
|
||||
SkOpSpanBase* span = next->span();
|
||||
next->setDeleted();
|
||||
if (span->ptT() == next) {
|
||||
span->upCast()->detach();
|
||||
}
|
||||
}
|
||||
|
||||
const SkOpSegment* SkOpPtT::segment() const {
|
||||
return span()->segment();
|
||||
}
|
||||
|
||||
SkOpSegment* SkOpPtT::segment() {
|
||||
return span()->segment();
|
||||
}
|
||||
|
||||
// find the starting or ending span with an existing loop of angles
|
||||
// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
|
||||
// FIXME? assert that only one other span has a valid windValue or oppValue
|
||||
void SkOpSpanBase::addSimpleAngle(bool checkFrom, SkChunkAlloc* allocator) {
|
||||
SkOpAngle* angle;
|
||||
if (checkFrom) {
|
||||
SkASSERT(this->final());
|
||||
if (this->fromAngle()) {
|
||||
SkASSERT(this->fromAngle()->loopCount() == 2);
|
||||
return;
|
||||
}
|
||||
angle = this->segment()->addEndSpan(allocator);
|
||||
} else {
|
||||
SkASSERT(this->t() == 0);
|
||||
SkOpSpan* span = this->upCast();
|
||||
if (span->toAngle()) {
|
||||
SkASSERT(span->toAngle()->loopCount() == 2);
|
||||
SkASSERT(!span->fromAngle());
|
||||
span->setFromAngle(span->toAngle()->next());
|
||||
return;
|
||||
}
|
||||
angle = this->segment()->addStartSpan(allocator);
|
||||
}
|
||||
SkOpPtT* ptT = this->ptT();
|
||||
SkOpSpanBase* oSpanBase;
|
||||
SkOpSpan* oSpan;
|
||||
SkOpSegment* other;
|
||||
do {
|
||||
ptT = ptT->next();
|
||||
oSpanBase = ptT->span();
|
||||
oSpan = oSpanBase->upCastable();
|
||||
other = oSpanBase->segment();
|
||||
if (oSpan && oSpan->windValue()) {
|
||||
break;
|
||||
}
|
||||
if (oSpanBase->t() == 0) {
|
||||
continue;
|
||||
}
|
||||
SkOpSpan* oFromSpan = oSpanBase->prev();
|
||||
SkASSERT(oFromSpan->t() < 1);
|
||||
if (oFromSpan->windValue()) {
|
||||
break;
|
||||
}
|
||||
} while (ptT != this->ptT());
|
||||
SkOpAngle* oAngle;
|
||||
if (checkFrom) {
|
||||
oAngle = other->addStartSpan(allocator);
|
||||
SkASSERT(oSpan && !oSpan->final());
|
||||
SkASSERT(oAngle == oSpan->toAngle());
|
||||
} else {
|
||||
oAngle = other->addEndSpan(allocator);
|
||||
SkASSERT(oAngle == oSpanBase->fromAngle());
|
||||
}
|
||||
angle->insert(oAngle);
|
||||
}
|
||||
|
||||
void SkOpSpanBase::align() {
|
||||
if (this->fAligned) {
|
||||
return;
|
||||
}
|
||||
SkASSERT(!zero_or_one(this->fPtT.fT));
|
||||
SkASSERT(this->fPtT.next());
|
||||
// if a linked pt/t pair has a t of zero or one, use it as the base for alignment
|
||||
SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
|
||||
while ((ptT = ptT->next()) != stopPtT) {
|
||||
if (zero_or_one(ptT->fT)) {
|
||||
SkOpSegment* segment = ptT->segment();
|
||||
SkASSERT(this->segment() != segment);
|
||||
SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT);
|
||||
if (ptT->fT) {
|
||||
segment->tail()->alignEnd(1, segment->lastPt());
|
||||
} else {
|
||||
segment->head()->alignEnd(0, segment->pts()[0]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
alignInner();
|
||||
this->fAligned = true;
|
||||
}
|
||||
|
||||
|
||||
// FIXME: delete spans that collapse
|
||||
// delete segments that collapse
|
||||
// delete contours that collapse
|
||||
void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) {
|
||||
SkASSERT(zero_or_one(t));
|
||||
SkOpSegment* segment = this->segment();
|
||||
SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
|
||||
alignInner();
|
||||
*segment->writablePt(!!t) = pt;
|
||||
SkOpPtT* ptT = &this->fPtT;
|
||||
SkASSERT(t == ptT->fT);
|
||||
SkASSERT(pt == ptT->fPt);
|
||||
SkOpPtT* test = ptT, * stopPtT = ptT;
|
||||
while ((test = test->next()) != stopPtT) {
|
||||
SkOpSegment* other = test->segment();
|
||||
if (other == this->segment()) {
|
||||
continue;
|
||||
}
|
||||
if (!zero_or_one(test->fT)) {
|
||||
continue;
|
||||
}
|
||||
*other->writablePt(!!test->fT) = pt;
|
||||
}
|
||||
this->fAligned = true;
|
||||
}
|
||||
|
||||
void SkOpSpanBase::alignInner() {
|
||||
// force the spans to share points and t values
|
||||
SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
|
||||
const SkPoint& pt = ptT->fPt;
|
||||
do {
|
||||
ptT->fPt = pt;
|
||||
const SkOpSpanBase* span = ptT->span();
|
||||
SkOpPtT* test = ptT;
|
||||
do {
|
||||
SkOpPtT* prev = test;
|
||||
if ((test = test->next()) == stopPtT) {
|
||||
break;
|
||||
}
|
||||
if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
|
||||
// omit aliases that alignment makes redundant
|
||||
if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
|
||||
SkASSERT(test->alias());
|
||||
prev->removeNext();
|
||||
test = prev;
|
||||
} else {
|
||||
SkASSERT(ptT->alias());
|
||||
stopPtT = ptT = ptT->remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
} while ((ptT = ptT->next()) != stopPtT);
|
||||
}
|
||||
|
||||
bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
|
||||
const SkOpPtT* start = &fPtT;
|
||||
const SkOpPtT* check = &span->fPtT;
|
||||
SkASSERT(start != check);
|
||||
const SkOpPtT* walk = start;
|
||||
while ((walk = walk->next()) != start) {
|
||||
if (walk == check) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkOpSpanBase::containsCoinEnd(const SkOpSegment* segment) const {
|
||||
SkASSERT(this->segment() != segment);
|
||||
const SkOpSpanBase* next = this;
|
||||
while ((next = next->fCoinEnd) != this) {
|
||||
if (next->segment() == segment) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SkOpContour* SkOpSpanBase::contour() const {
|
||||
return segment()->contour();
|
||||
}
|
||||
|
||||
SkOpDebugState* SkOpSpanBase::debugState() const {
|
||||
return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL);
|
||||
}
|
||||
|
||||
void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
|
||||
fSegment = segment;
|
||||
fPtT.init(this, t, pt, false);
|
||||
fCoinEnd = this;
|
||||
fFromAngle = NULL;
|
||||
fPrev = prev;
|
||||
fAligned = true;
|
||||
fChased = false;
|
||||
PATH_OPS_DEBUG_CODE(fCount = 1);
|
||||
PATH_OPS_DEBUG_CODE(fID = ++debugState()->fSpanID);
|
||||
}
|
||||
|
||||
// this pair of spans share a common t value or point; merge them and eliminate duplicates
|
||||
// this does not compute the best t or pt value; this merely moves all data into a single list
|
||||
void SkOpSpanBase::merge(SkOpSpan* span) {
|
||||
SkOpPtT* spanPtT = span->ptT();
|
||||
SkASSERT(this->t() != spanPtT->fT);
|
||||
SkASSERT(!zero_or_one(spanPtT->fT));
|
||||
span->detach();
|
||||
SkOpPtT* remainder = spanPtT->next();
|
||||
ptT()->insert(spanPtT);
|
||||
while (remainder != spanPtT) {
|
||||
SkOpPtT* next = remainder->next();
|
||||
SkOpPtT* compare = spanPtT->next();
|
||||
while (compare != spanPtT) {
|
||||
SkOpPtT* nextC = compare->next();
|
||||
if (nextC->span() == remainder->span() && nextC->fT == remainder->fT) {
|
||||
goto tryNextRemainder;
|
||||
}
|
||||
compare = nextC;
|
||||
}
|
||||
spanPtT->insert(remainder);
|
||||
tryNextRemainder:
|
||||
remainder = next;
|
||||
}
|
||||
}
|
||||
|
||||
void SkOpSpanBase::mergeBaseAttributes(SkOpSpanBase* span) {
|
||||
SkASSERT(!span->fChased);
|
||||
SkASSERT(!span->fFromAngle);
|
||||
if (this->upCastable() && span->upCastable()) {
|
||||
this->upCast()->mergeAttributes(span->upCast());
|
||||
}
|
||||
}
|
||||
|
||||
void SkOpSpan::applyCoincidence(SkOpSpan* opp) {
|
||||
SkASSERT(!final());
|
||||
SkASSERT(0); // incomplete
|
||||
}
|
||||
|
||||
bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
|
||||
SkASSERT(this->segment() != segment);
|
||||
const SkOpSpan* next = this;
|
||||
while ((next = next->fCoincident) != this) {
|
||||
if (next->segment() == segment) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkOpSpan::detach() {
|
||||
SkASSERT(!final());
|
||||
SkOpSpan* prev = this->prev();
|
||||
SkASSERT(prev);
|
||||
SkOpSpanBase* next = this->next();
|
||||
SkASSERT(next);
|
||||
prev->setNext(next);
|
||||
next->setPrev(prev);
|
||||
this->segment()->detach(this);
|
||||
this->ptT()->setDeleted();
|
||||
}
|
||||
|
||||
void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
|
||||
SkASSERT(t != 1);
|
||||
initBase(segment, prev, t, pt);
|
||||
fCoincident = this;
|
||||
fToAngle = NULL;
|
||||
fWindSum = fOppSum = SK_MinS32;
|
||||
fWindValue = 1;
|
||||
fOppValue = 0;
|
||||
fChased = fDone = false;
|
||||
segment->bumpCount();
|
||||
}
|
||||
|
||||
void SkOpSpan::mergeAttributes(SkOpSpan* span) {
|
||||
SkASSERT(!span->fToAngle);
|
||||
if (span->fCoincident) {
|
||||
this->insertCoincidence(span);
|
||||
}
|
||||
}
|
||||
|
||||
void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
|
||||
SkOpPtT* oppPtTEnd, bool flipped, SkChunkAlloc* allocator) {
|
||||
SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(allocator);
|
||||
SkOpSpanBase* coinEnd = coinPtTEnd->span();
|
||||
SkOpSpanBase* oppEnd = oppPtTEnd->span();
|
||||
SkOpSpan* coinStart = coinPtTStart->span()->upCast();
|
||||
SkASSERT(coinStart == coinStart->starter(coinEnd));
|
||||
SkOpSpan* oppStart = (flipped ? oppPtTEnd : oppPtTStart)->span()->upCast();
|
||||
SkASSERT(oppStart == oppStart->starter(oppEnd));
|
||||
coinStart->insertCoincidence(oppStart);
|
||||
coinEnd->insertCoinEnd(oppEnd);
|
||||
coinRec->fNext = this->fHead;
|
||||
coinRec->fCoinPtTStart = coinPtTStart;
|
||||
coinRec->fCoinPtTEnd = coinPtTEnd;
|
||||
coinRec->fOppPtTStart = oppPtTStart;
|
||||
coinRec->fOppPtTEnd = oppPtTEnd;
|
||||
coinRec->fFlipped = flipped;
|
||||
this->fHead = coinRec;
|
||||
}
|
||||
|
||||
bool SkOpCoincidence::contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
|
||||
SkOpPtT* oppPtTEnd, bool flipped) {
|
||||
SkCoincidentSpans* coin = fHead;
|
||||
if (!coin) {
|
||||
return false;
|
||||
}
|
||||
do {
|
||||
if (coin->fCoinPtTStart == coinPtTStart && coin->fCoinPtTEnd == coinPtTEnd
|
||||
&& coin->fOppPtTStart == oppPtTStart && coin->fOppPtTEnd == oppPtTEnd
|
||||
&& coin->fFlipped == flipped) {
|
||||
return true;
|
||||
}
|
||||
} while ((coin = coin->fNext));
|
||||
return false;
|
||||
}
|
||||
|
||||
// walk span sets in parallel, moving winding from one to the other
|
||||
void SkOpCoincidence::apply() {
|
||||
SkCoincidentSpans* coin = fHead;
|
||||
if (!coin) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
SkOpSpanBase* end = coin->fCoinPtTEnd->span();
|
||||
SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
|
||||
SkASSERT(start == start->starter(end));
|
||||
bool flipped = coin->fFlipped;
|
||||
SkOpSpanBase* oEnd = (flipped ? coin->fOppPtTStart : coin->fOppPtTEnd)->span();
|
||||
SkOpSpan* oStart = (flipped ? coin->fOppPtTEnd : coin->fOppPtTStart)->span()->upCast();
|
||||
SkASSERT(oStart == oStart->starter(oEnd));
|
||||
SkOpSegment* segment = start->segment();
|
||||
SkOpSegment* oSegment = oStart->segment();
|
||||
bool operandSwap = segment->operand() != oSegment->operand();
|
||||
if (flipped) {
|
||||
do {
|
||||
SkOpSpanBase* oNext = oStart->next();
|
||||
if (oNext == oEnd) {
|
||||
break;
|
||||
}
|
||||
oStart = oNext->upCast();
|
||||
} while (true);
|
||||
}
|
||||
bool isXor = segment->isXor();
|
||||
bool oppXor = oSegment->isXor();
|
||||
do {
|
||||
int windValue = start->windValue();
|
||||
int oWindValue = oStart->windValue();
|
||||
int oppValue = start->oppValue();
|
||||
int oOppValue = oStart->oppValue();
|
||||
// winding values are added or subtracted depending on direction and wind type
|
||||
// same or opposite values are summed depending on the operand value
|
||||
if (windValue >= oWindValue) {
|
||||
if (operandSwap) {
|
||||
SkTSwap(oWindValue, oOppValue);
|
||||
}
|
||||
if (flipped) {
|
||||
windValue -= oWindValue;
|
||||
oppValue -= oOppValue;
|
||||
} else {
|
||||
windValue += oWindValue;
|
||||
oppValue += oOppValue;
|
||||
}
|
||||
if (isXor) {
|
||||
windValue &= 1;
|
||||
}
|
||||
if (oppXor) {
|
||||
oppValue &= 1;
|
||||
}
|
||||
oWindValue = oOppValue = 0;
|
||||
} else {
|
||||
if (operandSwap) {
|
||||
SkTSwap(windValue, oppValue);
|
||||
}
|
||||
if (flipped) {
|
||||
oWindValue -= windValue;
|
||||
oOppValue -= oppValue;
|
||||
} else {
|
||||
oWindValue += windValue;
|
||||
oOppValue += oppValue;
|
||||
}
|
||||
if (isXor) {
|
||||
oOppValue &= 1;
|
||||
}
|
||||
if (oppXor) {
|
||||
oWindValue &= 1;
|
||||
}
|
||||
windValue = oppValue = 0;
|
||||
}
|
||||
start->setWindValue(windValue);
|
||||
start->setOppValue(oppValue);
|
||||
oStart->setWindValue(oWindValue);
|
||||
oStart->setOppValue(oOppValue);
|
||||
if (!windValue && !oppValue) {
|
||||
segment->markDone(start);
|
||||
}
|
||||
if (!oWindValue && !oOppValue) {
|
||||
oSegment->markDone(oStart);
|
||||
}
|
||||
SkOpSpanBase* next = start->next();
|
||||
SkOpSpanBase* oNext = flipped ? oStart->prev() : oStart->next();
|
||||
if (next == end) {
|
||||
break;
|
||||
}
|
||||
start = next->upCast();
|
||||
oStart = oNext->upCast();
|
||||
} while (true);
|
||||
} while ((coin = coin->fNext));
|
||||
}
|
||||
|
||||
void SkOpCoincidence::mark() {
|
||||
SkCoincidentSpans* coin = fHead;
|
||||
if (!coin) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
SkOpSpanBase* end = coin->fCoinPtTEnd->span();
|
||||
SkOpSpanBase* oldEnd = end;
|
||||
SkOpSpan* start = coin->fCoinPtTStart->span()->starter(&end);
|
||||
SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
|
||||
SkOpSpanBase* oOldEnd = oEnd;
|
||||
SkOpSpanBase* oStart = coin->fOppPtTStart->span()->starter(&oEnd);
|
||||
bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
|
||||
if (flipped) {
|
||||
SkTSwap(oStart, oEnd);
|
||||
}
|
||||
SkOpSpanBase* next = start;
|
||||
SkOpSpanBase* oNext = oStart;
|
||||
do {
|
||||
next = next->upCast()->next();
|
||||
oNext = flipped ? oNext->prev() : oNext->upCast()->next();
|
||||
if (next == end) {
|
||||
SkASSERT(oNext == oEnd);
|
||||
break;
|
||||
}
|
||||
if (!next->containsCoinEnd(oNext)) {
|
||||
next->insertCoinEnd(oNext);
|
||||
}
|
||||
SkOpSpan* nextSpan = next->upCast();
|
||||
SkOpSpan* oNextSpan = oNext->upCast();
|
||||
if (!nextSpan->containsCoincidence(oNextSpan)) {
|
||||
nextSpan->insertCoincidence(oNextSpan);
|
||||
}
|
||||
} while (true);
|
||||
} while ((coin = coin->fNext));
|
||||
}
|
175
src/pathops/SkPathOpsQuadSect.h
Normal file
175
src/pathops/SkPathOpsQuadSect.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkQuadSpan_DEFINE
|
||||
#define SkQuadSpan_DEFINE
|
||||
|
||||
#include "SkChunkAlloc.h"
|
||||
#include "SkPathOpsRect.h"
|
||||
#include "SkPathOpsQuad.h"
|
||||
#include "SkTArray.h"
|
||||
|
||||
class SkIntersections;
|
||||
|
||||
class SkQuadCoincident {
|
||||
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 SkDQuad& quad1, double t, const SkDPoint& qPt, const SkDQuad& quad2);
|
||||
|
||||
private:
|
||||
SkDPoint fPerpPt;
|
||||
double fPerpT; // perpendicular intersection on opposite quad
|
||||
bool fCoincident;
|
||||
};
|
||||
|
||||
class SkQuadSect; // used only by debug id
|
||||
|
||||
class SkQuadSpan {
|
||||
public:
|
||||
void init(const SkDQuad& quad);
|
||||
void initBounds(const SkDQuad& quad);
|
||||
|
||||
bool contains(double t) const {
|
||||
return !! const_cast<SkQuadSpan*>(this)->innerFind(t);
|
||||
}
|
||||
|
||||
bool contains(const SkQuadSpan* span) const;
|
||||
|
||||
SkQuadSpan* find(double t) {
|
||||
SkQuadSpan* result = innerFind(t);
|
||||
SkASSERT(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool intersects(const SkQuadSpan* span) const;
|
||||
|
||||
const SkQuadSpan* next() const {
|
||||
return fNext;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
fBounded.reset();
|
||||
}
|
||||
|
||||
bool split(SkQuadSpan* work) {
|
||||
return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
|
||||
}
|
||||
|
||||
bool splitAt(SkQuadSpan* work, double t);
|
||||
bool tightBoundsIntersects(const SkQuadSpan* span) const;
|
||||
|
||||
// implementation is for testing only
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
bool hullIntersects(const SkDQuad& q2) const;
|
||||
SkQuadSpan* innerFind(double t);
|
||||
bool linearIntersects(const SkDQuad& q2) const;
|
||||
|
||||
// implementation is for testing only
|
||||
#if DEBUG_BINARY_QUAD
|
||||
int debugID(const SkQuadSect* ) const { return fDebugID; }
|
||||
#else
|
||||
int debugID(const SkQuadSect* ) const;
|
||||
#endif
|
||||
void dump(const SkQuadSect* ) const;
|
||||
void dumpID(const SkQuadSect* ) const;
|
||||
|
||||
#if DEBUG_BINARY_QUAD
|
||||
void validate() const;
|
||||
#endif
|
||||
|
||||
SkDQuad fPart;
|
||||
SkQuadCoincident fCoinStart;
|
||||
SkQuadCoincident fCoinEnd;
|
||||
SkSTArray<4, SkQuadSpan*, true> fBounded;
|
||||
SkQuadSpan* fPrev;
|
||||
SkQuadSpan* fNext;
|
||||
SkDRect fBounds;
|
||||
double fStartT;
|
||||
double fEndT;
|
||||
double fBoundsMax;
|
||||
bool fCollapsed;
|
||||
bool fHasPerp;
|
||||
mutable bool fIsLinear;
|
||||
#if DEBUG_BINARY_QUAD
|
||||
int fDebugID;
|
||||
bool fDebugDeleted;
|
||||
#endif
|
||||
friend class SkQuadSect;
|
||||
};
|
||||
|
||||
class SkQuadSect {
|
||||
public:
|
||||
SkQuadSect(const SkDQuad& quad PATH_OPS_DEBUG_PARAMS(int id));
|
||||
static void BinarySearch(SkQuadSect* sect1, SkQuadSect* sect2, SkIntersections* intersections);
|
||||
|
||||
// for testing only
|
||||
void dumpQuads() const;
|
||||
private:
|
||||
SkQuadSpan* addOne();
|
||||
bool binarySearchCoin(const SkQuadSect& , double tStart, double tStep, double* t, double* oppT);
|
||||
SkQuadSpan* boundsMax() const;
|
||||
void coincidentCheck(SkQuadSect* sect2);
|
||||
bool intersects(const SkQuadSpan* span, const SkQuadSect* opp, const SkQuadSpan* oppSpan) const;
|
||||
void onCurveCheck(SkQuadSect* sect2, SkQuadSpan* first, SkQuadSpan* last);
|
||||
void recoverCollapsed();
|
||||
void removeSpan(SkQuadSpan* span);
|
||||
void removeOne(const SkQuadSpan* test, SkQuadSpan* span);
|
||||
void removeSpans(SkQuadSpan* span, SkQuadSect* opp);
|
||||
void setPerp(const SkDQuad& opp, SkQuadSpan* first, SkQuadSpan* last);
|
||||
const SkQuadSpan* tail() const;
|
||||
void trim(SkQuadSpan* span, SkQuadSect* opp);
|
||||
|
||||
// for testing only
|
||||
void dump() const;
|
||||
void dumpBoth(const SkQuadSect& opp) const;
|
||||
void dumpBoth(const SkQuadSect* opp) const;
|
||||
|
||||
#if DEBUG_BINARY_QUAD
|
||||
int debugID() const { return fDebugID; }
|
||||
void validate() const;
|
||||
#else
|
||||
int debugID() const { return 0; }
|
||||
#endif
|
||||
const SkDQuad& fQuad;
|
||||
SkChunkAlloc fHeap;
|
||||
SkQuadSpan* fHead;
|
||||
SkQuadSpan* fDeleted;
|
||||
int fActiveCount;
|
||||
#if DEBUG_BINARY_QUAD
|
||||
int fDebugID;
|
||||
int fDebugCount;
|
||||
int fDebugAllocatedCount;
|
||||
#endif
|
||||
friend class SkQuadSpan; // only used by debug id
|
||||
};
|
||||
|
||||
#endif
|
15
src/pathops/SkPathOpsTCubicSect.cpp
Normal file
15
src/pathops/SkPathOpsTCubicSect.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 "SkPathOpsTSect.h"
|
||||
|
||||
int SkIntersections::intersectB(const SkDCubic& cubic1, const SkDCubic& cubic2) {
|
||||
SkTSect<SkDCubic> sect1(cubic1 PATH_OPS_DEBUG_PARAMS(1));
|
||||
SkTSect<SkDCubic> sect2(cubic2 PATH_OPS_DEBUG_PARAMS(2));
|
||||
SkTSect<SkDCubic>::BinarySearch(§1, §2, this);
|
||||
return used();
|
||||
}
|
15
src/pathops/SkPathOpsTQuadSect.cpp
Normal file
15
src/pathops/SkPathOpsTQuadSect.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 "SkPathOpsTSect.h"
|
||||
|
||||
int SkIntersections::intersectB(const SkDQuad& quad1, const SkDQuad& quad2) {
|
||||
SkTSect<SkDQuad> sect1(quad1 PATH_OPS_DEBUG_PARAMS(1));
|
||||
SkTSect<SkDQuad> sect2(quad2 PATH_OPS_DEBUG_PARAMS(2));
|
||||
SkTSect<SkDQuad>::BinarySearch(§1, §2, this);
|
||||
return used();
|
||||
}
|
1211
src/pathops/SkPathOpsTSect.h
Normal file
1211
src/pathops/SkPathOpsTSect.h
Normal file
File diff suppressed because it is too large
Load Diff
2435
tests/PathOpsBuildUseTest.cpp
Normal file
2435
tests/PathOpsBuildUseTest.cpp
Normal file
File diff suppressed because it is too large
Load Diff
73
tests/PathOpsBuilderTest.cpp
Normal file
73
tests/PathOpsBuilderTest.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 "PathOpsTestCommon.h"
|
||||
#include "Test.h"
|
||||
|
||||
DEF_TEST(PathOpsBuilder, reporter) {
|
||||
SkOpBuilder builder;
|
||||
SkPath result;
|
||||
REPORTER_ASSERT(reporter, builder.resolve(&result));
|
||||
REPORTER_ASSERT(reporter, result.isEmpty());
|
||||
|
||||
builder.add(result, kDifference_PathOp);
|
||||
REPORTER_ASSERT(reporter, builder.resolve(&result));
|
||||
REPORTER_ASSERT(reporter, result.isEmpty());
|
||||
|
||||
builder.add(result, kUnion_PathOp);
|
||||
REPORTER_ASSERT(reporter, builder.resolve(&result));
|
||||
REPORTER_ASSERT(reporter, result.isEmpty());
|
||||
|
||||
SkPath rectPath;
|
||||
rectPath.addRect(0, 1, 2, 3, SkPath::kCW_Direction);
|
||||
builder.add(rectPath, kUnion_PathOp);
|
||||
REPORTER_ASSERT(reporter, builder.resolve(&result));
|
||||
bool closed;
|
||||
SkPath::Direction dir;
|
||||
REPORTER_ASSERT(reporter, result.isRect(NULL, &closed, &dir));
|
||||
REPORTER_ASSERT(reporter, closed);
|
||||
REPORTER_ASSERT(reporter, dir == SkPath::kCW_Direction);
|
||||
REPORTER_ASSERT(reporter, rectPath == result);
|
||||
|
||||
rectPath.reset();
|
||||
rectPath.addRect(0, 1, 2, 3, SkPath::kCCW_Direction);
|
||||
builder.add(rectPath, kUnion_PathOp);
|
||||
REPORTER_ASSERT(reporter, builder.resolve(&result));
|
||||
REPORTER_ASSERT(reporter, result.isRect(NULL, &closed, &dir));
|
||||
REPORTER_ASSERT(reporter, closed);
|
||||
REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
|
||||
REPORTER_ASSERT(reporter, rectPath == result);
|
||||
|
||||
builder.add(rectPath, kDifference_PathOp);
|
||||
REPORTER_ASSERT(reporter, builder.resolve(&result));
|
||||
REPORTER_ASSERT(reporter, result.isEmpty());
|
||||
|
||||
SkPath rect2, rect3;
|
||||
rect2.addRect(2, 1, 4, 3, SkPath::kCW_Direction);
|
||||
rect3.addRect(4, 1, 5, 3, SkPath::kCCW_Direction);
|
||||
builder.add(rectPath, kUnion_PathOp);
|
||||
builder.add(rect2, kUnion_PathOp);
|
||||
builder.add(rect3, kUnion_PathOp);
|
||||
REPORTER_ASSERT(reporter, builder.resolve(&result));
|
||||
REPORTER_ASSERT(reporter, result.isRect(NULL, &closed, &dir));
|
||||
REPORTER_ASSERT(reporter, closed);
|
||||
SkRect expected;
|
||||
expected.set(0, 1, 5, 3);
|
||||
REPORTER_ASSERT(reporter, result.getBounds() == expected);
|
||||
|
||||
SkPath circle1, circle2, circle3;
|
||||
circle1.addCircle(5, 6, 4, SkPath::kCW_Direction);
|
||||
circle2.addCircle(7, 4, 8, SkPath::kCCW_Direction);
|
||||
circle3.addCircle(6, 5, 6, SkPath::kCW_Direction);
|
||||
SkPath opCompare;
|
||||
Op(circle1, circle2, kUnion_PathOp, &opCompare);
|
||||
Op(opCompare, circle3, kDifference_PathOp, &opCompare);
|
||||
builder.add(circle1, kUnion_PathOp);
|
||||
builder.add(circle2, kUnion_PathOp);
|
||||
builder.add(circle3, kDifference_PathOp);
|
||||
REPORTER_ASSERT(reporter, builder.resolve(&result));
|
||||
REPORTER_ASSERT(reporter, opCompare == result);
|
||||
}
|
84
tests/PathOpsTSectDebug.h
Normal file
84
tests/PathOpsTSectDebug.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 "SkPathOpsTSect.h"
|
||||
|
||||
template<typename TCurve>
|
||||
void SkTSect<TCurve>::dump() const {
|
||||
SkDebugf("id=%d", debugID());
|
||||
const SkTSpan<TCurve>* test = fHead;
|
||||
if (!test) {
|
||||
SkDebugf(" (empty)");
|
||||
return;
|
||||
}
|
||||
do {
|
||||
SkDebugf(" ");
|
||||
test->dump(this);
|
||||
} while ((test = test->next()));
|
||||
}
|
||||
|
||||
template<typename TCurve>
|
||||
void SkTSect<TCurve>::dumpBoth(const SkTSect& opp) const {
|
||||
dump();
|
||||
SkDebugf(" ");
|
||||
opp.dump();
|
||||
SkDebugf("\n");
|
||||
}
|
||||
|
||||
template<typename TCurve>
|
||||
void SkTSect<TCurve>::dumpBoth(const SkTSect* opp) const {
|
||||
dumpBoth(*opp);
|
||||
}
|
||||
|
||||
template<typename TCurve>
|
||||
void SkTSect<TCurve>::dumpCurves() const {
|
||||
const SkTSpan<TCurve>* test = fHead;
|
||||
do {
|
||||
test->fPart.dump();
|
||||
} while ((test = test->next()));
|
||||
}
|
||||
|
||||
#if !DEBUG_T_SECT
|
||||
template<typename TCurve>
|
||||
int SkTSpan<TCurve>::debugID(const SkTSect<TCurve>* sect) const {
|
||||
if (!sect) {
|
||||
return -1;
|
||||
}
|
||||
int id = 1;
|
||||
const SkTSpan* test = sect->fHead;
|
||||
while (test && test != this) {
|
||||
++id;
|
||||
test = test->fNext;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename TCurve>
|
||||
void SkTSpan<TCurve>::dumpID(const SkTSect<TCurve>* sect) const {
|
||||
if (fCoinStart.isCoincident()) {
|
||||
SkDebugf("%c", '*');
|
||||
}
|
||||
SkDebugf("%d", debugID(sect));
|
||||
if (fCoinEnd.isCoincident()) {
|
||||
SkDebugf("%c", '*');
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TCurve>
|
||||
void SkTSpan<TCurve>::dump(const SkTSect<TCurve>* sect) const {
|
||||
dumpID(sect);
|
||||
SkDebugf("=(%g,%g) [", fStartT, fEndT);
|
||||
for (int index = 0; index < fBounded.count(); ++index) {
|
||||
SkTSpan* span = fBounded[index];
|
||||
span->dumpID(sect);
|
||||
if (index < fBounded.count() - 1) {
|
||||
SkDebugf(",");
|
||||
}
|
||||
}
|
||||
SkDebugf("]");
|
||||
}
|
78
tests/PathOpsThreeWayTest.cpp
Normal file
78
tests/PathOpsThreeWayTest.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 "SkIntersections.h"
|
||||
#include "SkTDArray.h"
|
||||
#include "Test.h"
|
||||
|
||||
// check intersections for consistency
|
||||
|
||||
struct Curve {
|
||||
int ptCount;
|
||||
SkDCubic curve; // largest can hold lines / quads/ cubics
|
||||
};
|
||||
|
||||
static const Curve testSet0[] = { // extracted from skpClip2
|
||||
{4, {{{134,11414}, {131.990234,11414}, {130.32666,11415.4824}, {130.042755,11417.4131}}} },
|
||||
{4, {{{130.042755,11417.4131}, {130.233124,11418.3193}, {131.037079,11419}, {132,11419}}} },
|
||||
{4, {{{132,11419}, {130.895432,11419}, {130,11418.1045}, {130,11417}}} },
|
||||
};
|
||||
|
||||
static const Curve testSet1[] = { // extracted from cubicOp85i
|
||||
{4, {{{3,4}, {1,5}, {4,3}, {6,4}}} },
|
||||
{1, {{{6,4}, {3,4}}} },
|
||||
{4, {{{3,4}, {4,6}, {4,3}, {5,1}}} },
|
||||
{1, {{{5,1}, {3,4}}} },
|
||||
};
|
||||
|
||||
static const struct TestSet {
|
||||
const Curve* tests;
|
||||
int testCount;
|
||||
} testSets[] = {
|
||||
{ testSet0, (int) SK_ARRAY_COUNT(testSet0) },
|
||||
{ testSet1, (int) SK_ARRAY_COUNT(testSet1) },
|
||||
};
|
||||
|
||||
static const int testSetsCount = (int) SK_ARRAY_COUNT(testSets);
|
||||
|
||||
static void testSetTest(skiatest::Reporter* reporter, int index) {
|
||||
const TestSet& testSet = testSets[index];
|
||||
int testCount = testSet.testCount;
|
||||
SkASSERT(testCount > 1);
|
||||
SkTDArray<SkIntersections> combos;
|
||||
for (int outer = 0; outer < testCount - 1; ++outer) {
|
||||
const Curve& oTest = testSet.tests[outer];
|
||||
for (int inner = outer + 1; inner < testCount; ++inner) {
|
||||
const Curve& iTest = testSet.tests[inner];
|
||||
SkIntersections* i = combos.append();
|
||||
sk_bzero(i, sizeof(SkIntersections));
|
||||
if (oTest.ptCount == 1 && iTest.ptCount == 1) {
|
||||
i->intersect(*(const SkDLine*) &oTest.curve, *(const SkDLine*) &iTest.curve);
|
||||
} else if (oTest.ptCount == 1 && iTest.ptCount == 4) {
|
||||
i->intersect(iTest.curve, *(const SkDLine*) &oTest.curve);
|
||||
} else if (oTest.ptCount == 4 && iTest.ptCount == 1) {
|
||||
i->intersect(oTest.curve, *(const SkDLine*) &oTest.curve);
|
||||
} else if (oTest.ptCount == 4 && iTest.ptCount == 4) {
|
||||
i->intersectB(oTest.curve, iTest.curve);
|
||||
} else {
|
||||
SkASSERT(0);
|
||||
}
|
||||
// i->dump();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(PathOpsThreeWay, reporter) {
|
||||
for (int index = 0; index < testSetsCount; ++index) {
|
||||
testSetTest(reporter, index);
|
||||
reporter->bumpTestCount();
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(PathOpsThreeWayOneOff, reporter) {
|
||||
int index = 1;
|
||||
testSetTest(reporter, index);
|
||||
}
|
Loading…
Reference in New Issue
Block a user