work in progress
git-svn-id: http://skia.googlecode.com/svn/trunk@3291 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
f6fc404409
commit
cd4421df50
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
static bool gShowDebugf = true; // FIXME: remove once debugging is complete
|
static bool gShowDebugf = true; // FIXME: remove once debugging is complete
|
||||||
static bool gShowPath = false;
|
static bool gShowPath = false;
|
||||||
static bool gDebugLessThan = false;
|
static bool gDebugLessThan = true;
|
||||||
|
|
||||||
static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
|
static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
|
||||||
double aRange[2], double bRange[2]) {
|
double aRange[2], double bRange[2]) {
|
||||||
@ -30,11 +30,12 @@ static int LineIntersect(const SkPoint a[2], SkScalar y, double aRange[2]) {
|
|||||||
return horizontalIntersect(aLine, y, aRange);
|
return horizontalIntersect(aLine, y, aRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SkScalar LineXAtT(const SkPoint a[2], double t) {
|
static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
|
||||||
_Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
|
_Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
|
||||||
double x;
|
double x, y;
|
||||||
xy_at_t(aLine, t, x, *(double*) 0);
|
xy_at_t(aLine, t, x, y);
|
||||||
return SkDoubleToScalar(x);
|
out->fX = SkDoubleToScalar(x);
|
||||||
|
out->fY = SkDoubleToScalar(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SkScalar LineYAtT(const SkPoint a[2], double t) {
|
static SkScalar LineYAtT(const SkPoint a[2], double t) {
|
||||||
@ -419,10 +420,11 @@ struct InEdge {
|
|||||||
size_t tCount = intercepts.fTs.count();
|
size_t tCount = intercepts.fTs.count();
|
||||||
for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
|
for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
|
||||||
if (t <= intercepts.fTs[idx2]) {
|
if (t <= intercepts.fTs[idx2]) {
|
||||||
if (t < intercepts.fTs[idx2]) {
|
double delta = intercepts.fTs[idx2] - t;
|
||||||
|
if (delta > 0) {
|
||||||
*intercepts.fTs.insert(idx2) = t;
|
*intercepts.fTs.insert(idx2) = t;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
return foundIntercept;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tCount == 0 || t > intercepts.fTs[tCount - 1]) {
|
if (tCount == 0 || t > intercepts.fTs[tCount - 1]) {
|
||||||
@ -649,6 +651,12 @@ struct WorkEdge {
|
|||||||
fPts += *fVerb++;
|
fPts += *fVerb++;
|
||||||
return fVerb != fEdge->fVerbs.end();
|
return fVerb != fEdge->fVerbs.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkPath::Verb lastVerb() const {
|
||||||
|
SkASSERT(fVerb > fEdge->fVerbs.begin());
|
||||||
|
return (SkPath::Verb) fVerb[-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SkPath::Verb verb() const {
|
SkPath::Verb verb() const {
|
||||||
return (SkPath::Verb) *fVerb;
|
return (SkPath::Verb) *fVerb;
|
||||||
@ -670,16 +678,73 @@ struct WorkEdge {
|
|||||||
// always constructed with SkTDArray because new edges are inserted
|
// always constructed with SkTDArray because new edges are inserted
|
||||||
// this may be a inappropriate optimization, suggesting that a separate array of
|
// this may be a inappropriate optimization, suggesting that a separate array of
|
||||||
// ActiveEdge* may be faster to insert and search
|
// ActiveEdge* may be faster to insert and search
|
||||||
|
|
||||||
|
// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
|
||||||
|
// as active edges are introduced, only local sorting should be required
|
||||||
struct ActiveEdge {
|
struct ActiveEdge {
|
||||||
|
// OPTIMIZATION: fold return statements into one
|
||||||
bool operator<(const ActiveEdge& rh) const {
|
bool operator<(const ActiveEdge& rh) const {
|
||||||
return fXAbove != rh.fXAbove ? fXAbove < rh.fXAbove
|
if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY
|
||||||
: fXBelow < rh.fXBelow;
|
&& fBelow.fY < rh.fBelow.fY
|
||||||
|
|| fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY
|
||||||
|
&& rh.fBelow.fY < fBelow.fY) {
|
||||||
|
// FIXME: need to compute distance, not check for points equal
|
||||||
|
const SkPoint& check = rh.fBelow.fY <= fBelow.fY
|
||||||
|
&& fBelow != rh.fBelow ? rh.fBelow :
|
||||||
|
rh.fAbove;
|
||||||
|
if (gDebugLessThan) {
|
||||||
|
SkDebugf("%s < %c %cthis (%d){%1.2g,%1.2g %1.2g,%1.2g}"
|
||||||
|
" < rh (%d){%1.2g,%1.2g %1.2g,%1.2g}\n", __FUNCTION__,
|
||||||
|
rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
|
||||||
|
(check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
|
||||||
|
< (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)
|
||||||
|
? ' ' : '!',
|
||||||
|
fIndex, fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
|
||||||
|
rh.fIndex, rh.fAbove.fX, rh.fAbove.fY,
|
||||||
|
rh.fBelow.fX, rh.fBelow.fY);
|
||||||
|
}
|
||||||
|
return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
|
||||||
|
< (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX);
|
||||||
|
}
|
||||||
|
// FIXME: need to compute distance, not check for points equal
|
||||||
|
const SkPoint& check = fBelow.fY <= rh.fBelow.fY
|
||||||
|
&& fBelow != rh.fBelow ? fBelow : fAbove;
|
||||||
|
if (gDebugLessThan) {
|
||||||
|
SkDebugf("%s > %c %cthis (%d){%1.2g,%1.2g %1.2g,%1.2g}"
|
||||||
|
" < rh (%d){%1.2g,%1.2g %1.2g,%1.2g}\n", __FUNCTION__,
|
||||||
|
fBelow.fY <= rh.fBelow.fY & fBelow != rh.fBelow ? 'B' : 'A',
|
||||||
|
(rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
|
||||||
|
< (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)
|
||||||
|
? ' ' : '!',
|
||||||
|
fIndex, fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
|
||||||
|
rh.fIndex, rh.fAbove.fX, rh.fAbove.fY,
|
||||||
|
rh.fBelow.fX, rh.fBelow.fY);
|
||||||
|
}
|
||||||
|
return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
|
||||||
|
< (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void calcLeft() {
|
bool advanceT() {
|
||||||
|
SkASSERT(fTIndex <= fTs->count());
|
||||||
|
return ++fTIndex <= fTs->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool advance() {
|
||||||
|
// FIXME: flip sense of next
|
||||||
|
bool result = fWorkEdge.advance();
|
||||||
|
fDone = !result;
|
||||||
|
initT();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void calcLeft(SkScalar y) {
|
||||||
// OPTIMIZE: put a kDone_Verb at the end of the verb list?
|
// OPTIMIZE: put a kDone_Verb at the end of the verb list?
|
||||||
if (fDone)
|
if (done(y))
|
||||||
return; // nothing to do; use last
|
return; // nothing to do; use last
|
||||||
|
calcLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
void calcLeft() {
|
||||||
switch (fWorkEdge.verb()) {
|
switch (fWorkEdge.verb()) {
|
||||||
case SkPath::kLine_Verb: {
|
case SkPath::kLine_Verb: {
|
||||||
// OPTIMIZATION: if fXAbove, fXBelow have already been computed
|
// OPTIMIZATION: if fXAbove, fXBelow have already been computed
|
||||||
@ -688,9 +753,10 @@ struct ActiveEdge {
|
|||||||
// If both edges have T values < 1, check x at next T (fXBelow).
|
// If both edges have T values < 1, check x at next T (fXBelow).
|
||||||
int add = (fTIndex <= fTs->count()) - 1;
|
int add = (fTIndex <= fTs->count()) - 1;
|
||||||
double tAbove = t(fTIndex + add);
|
double tAbove = t(fTIndex + add);
|
||||||
fXAbove = LineXAtT(fWorkEdge.fPts, tAbove);
|
// OPTIMIZATION: may not need Y
|
||||||
|
LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
|
||||||
double tBelow = t(fTIndex - ~add);
|
double tBelow = t(fTIndex - ~add);
|
||||||
fXBelow = LineXAtT(fWorkEdge.fPts, tBelow);
|
LineXYAtT(fWorkEdge.fPts, tBelow, &fBelow);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -699,6 +765,10 @@ struct ActiveEdge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool done(SkScalar y) {
|
||||||
|
return fDone || fYBottom > y;
|
||||||
|
}
|
||||||
|
|
||||||
void init(const InEdge* edge) {
|
void init(const InEdge* edge) {
|
||||||
fWorkEdge.init(edge);
|
fWorkEdge.init(edge);
|
||||||
initT();
|
initT();
|
||||||
@ -717,16 +787,30 @@ struct ActiveEdge {
|
|||||||
fTIndex = 0;
|
fTIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isCoincidentWith(const ActiveEdge* edge) const {
|
// OPTIMIZATION: record if two edges are coincident when the are intersected
|
||||||
if (fXAbove != edge->fXAbove || fXBelow != edge->fXBelow) {
|
// It's unclear how to do this -- seems more complicated than recording the
|
||||||
|
// t values, since the same t values could exist intersecting non-coincident
|
||||||
|
// edges.
|
||||||
|
bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
|
||||||
|
if (fAbove.fX != edge->fAbove.fX || fBelow.fX != edge->fBelow.fX) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
switch (fWorkEdge.verb()) {
|
uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
|
||||||
|
uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
|
||||||
|
: edge->fWorkEdge.verb();
|
||||||
|
if (verb != edgeVerb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (verb) {
|
||||||
case SkPath::kLine_Verb: {
|
case SkPath::kLine_Verb: {
|
||||||
return (fWorkEdge.fPts[0].fX - fWorkEdge.fPts[1].fX) *
|
int offset = fDone ? -1 : 1;
|
||||||
(edge->fWorkEdge.fPts[0].fY - edge->fWorkEdge.fPts[1].fY) ==
|
int edgeOffset = edge->fDone ? -1 : 1;
|
||||||
(fWorkEdge.fPts[0].fY - fWorkEdge.fPts[1].fY) *
|
const SkPoint* pts = fWorkEdge.fPts;
|
||||||
(edge->fWorkEdge.fPts[0].fX - edge->fWorkEdge.fPts[1].fX);
|
const SkPoint* edgePts = edge->fWorkEdge.fPts;
|
||||||
|
return (pts->fX - pts[offset].fX)
|
||||||
|
* (edgePts->fY - edgePts[edgeOffset].fY)
|
||||||
|
== (pts->fY - pts[offset].fY)
|
||||||
|
* (edgePts->fX - edgePts[edgeOffset].fX);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// FIXME: add support for all curve types
|
// FIXME: add support for all curve types
|
||||||
@ -734,24 +818,27 @@ struct ActiveEdge {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
|
||||||
|
if (fBelow.fY >= bottom || fDone || edge->fDone) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ActiveEdge thisWork = *this;
|
||||||
|
ActiveEdge edgeWork = *edge;
|
||||||
|
while ((thisWork.advanceT() || thisWork.advance())
|
||||||
|
&& (edgeWork.advanceT() || edgeWork.advance())) {
|
||||||
|
thisWork.calcLeft();
|
||||||
|
edgeWork.calcLeft();
|
||||||
|
if (thisWork < edgeWork) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (edgeWork < thisWork) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool advanceT() {
|
|
||||||
SkASSERT(fTIndex <= fTs->count());
|
|
||||||
return ++fTIndex <= fTs->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool advance() {
|
|
||||||
// FIXME: flip sense of next
|
|
||||||
bool result = fWorkEdge.advance();
|
|
||||||
fDone = !result;
|
|
||||||
initT();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool done(SkScalar y) {
|
|
||||||
return fDone || fYBottom > y;
|
|
||||||
}
|
|
||||||
|
|
||||||
double nextT() {
|
double nextT() {
|
||||||
SkASSERT(fTIndex <= fTs->count());
|
SkASSERT(fTIndex <= fTs->count());
|
||||||
return t(fTIndex + 1);
|
return t(fTIndex + 1);
|
||||||
@ -779,12 +866,13 @@ struct ActiveEdge {
|
|||||||
|
|
||||||
WorkEdge fWorkEdge;
|
WorkEdge fWorkEdge;
|
||||||
const SkTDArray<double>* fTs;
|
const SkTDArray<double>* fTs;
|
||||||
SkScalar fXAbove;
|
SkPoint fAbove;
|
||||||
SkScalar fXBelow;
|
SkPoint fBelow;
|
||||||
SkScalar fYBottom;
|
SkScalar fYBottom;
|
||||||
int fTIndex;
|
int fTIndex;
|
||||||
bool fSkip;
|
bool fSkip;
|
||||||
bool fDone;
|
bool fDone;
|
||||||
|
int fIndex; // REMOVE: debugging only
|
||||||
};
|
};
|
||||||
|
|
||||||
static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
|
static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
|
||||||
@ -959,8 +1047,7 @@ static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
|
|||||||
}
|
}
|
||||||
edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
|
edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
|
||||||
*edgeList.append() = &edgeSentinel;
|
*edgeList.append() = &edgeSentinel;
|
||||||
++edgeCount;
|
QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
|
||||||
QSort<InEdge>(edgeList.begin(), edgeCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -977,7 +1064,8 @@ static void skipCoincidence(int lastWinding, int winding, int windingMask,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
|
static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
|
||||||
SkTDArray<ActiveEdge*>& edgeList, int windingMask) {
|
SkTDArray<ActiveEdge*>& edgeList, int windingMask, SkScalar y,
|
||||||
|
SkScalar bottom) {
|
||||||
size_t edgeCount = activeEdges.count();
|
size_t edgeCount = activeEdges.count();
|
||||||
if (edgeCount == 0) {
|
if (edgeCount == 0) {
|
||||||
return;
|
return;
|
||||||
@ -985,11 +1073,12 @@ static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
|
|||||||
size_t index;
|
size_t index;
|
||||||
for (index = 0; index < edgeCount; ++index) {
|
for (index = 0; index < edgeCount; ++index) {
|
||||||
ActiveEdge& activeEdge = activeEdges[index];
|
ActiveEdge& activeEdge = activeEdges[index];
|
||||||
activeEdge.calcLeft();
|
activeEdge.calcLeft(y);
|
||||||
activeEdge.fSkip = false;
|
activeEdge.fSkip = false;
|
||||||
|
activeEdge.fIndex = index; // REMOVE: debugging only
|
||||||
*edgeList.append() = &activeEdge;
|
*edgeList.append() = &activeEdge;
|
||||||
}
|
}
|
||||||
QSort<ActiveEdge>(edgeList.begin(), edgeCount);
|
QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
|
||||||
// remove coincident edges
|
// remove coincident edges
|
||||||
// OPTIMIZE: remove edges? This is tricky because the current logic expects
|
// OPTIMIZE: remove edges? This is tricky because the current logic expects
|
||||||
// the winding count to be maintained while skipping coincident edges. In
|
// the winding count to be maintained while skipping coincident edges. In
|
||||||
@ -1003,7 +1092,16 @@ static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
|
|||||||
for (index = 1; index < edgeCount; ++index) {
|
for (index = 1; index < edgeCount; ++index) {
|
||||||
winding += activePtr->fWorkEdge.winding();
|
winding += activePtr->fWorkEdge.winding();
|
||||||
ActiveEdge* nextPtr = edgeList[index];
|
ActiveEdge* nextPtr = edgeList[index];
|
||||||
if (activePtr->isCoincidentWith(nextPtr)) {
|
if (activePtr->isCoincidentWith(nextPtr, y)) {
|
||||||
|
// the coincident edges may not have been sorted above -- advance
|
||||||
|
// the edges and resort if needed
|
||||||
|
// OPTIMIZE: if sorting is done incrementally as new edges are added
|
||||||
|
// and not all at once as is done here, fold this test into the
|
||||||
|
// current less than test.
|
||||||
|
if (activePtr->swapCoincident(nextPtr, bottom)) {
|
||||||
|
SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
|
||||||
|
SkTSwap<ActiveEdge*>(activePtr, nextPtr);
|
||||||
|
}
|
||||||
if (!firstCoincident) {
|
if (!firstCoincident) {
|
||||||
firstCoincident = activePtr;
|
firstCoincident = activePtr;
|
||||||
}
|
}
|
||||||
@ -1041,7 +1139,16 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
|
|||||||
int lastWinding = winding;
|
int lastWinding = winding;
|
||||||
winding += wt.winding();
|
winding += wt.winding();
|
||||||
if (activePtr->done(y)) {
|
if (activePtr->done(y)) {
|
||||||
continue;
|
// FIXME: if this is successful, rewrite done to take bottom as well
|
||||||
|
if (activePtr->fDone) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (activePtr->fYBottom >= bottom) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0) {
|
||||||
|
SkDebugf("%s bot %g,%g\n", __FUNCTION__, activePtr->fYBottom, bottom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int opener = (lastWinding & windingMask) == 0;
|
int opener = (lastWinding & windingMask) == 0;
|
||||||
bool closer = (winding & windingMask) == 0;
|
bool closer = (winding & windingMask) == 0;
|
||||||
@ -1077,6 +1184,7 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
|
|||||||
}
|
}
|
||||||
outBuilder.addLine(clipped);
|
outBuilder.addLine(clipped);
|
||||||
}
|
}
|
||||||
|
activePtr->fSkip = false;
|
||||||
} else {
|
} else {
|
||||||
// FIXME: add all curve types
|
// FIXME: add all curve types
|
||||||
SkASSERT(0);
|
SkASSERT(0);
|
||||||
@ -1119,7 +1227,7 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
|
|||||||
addIntersectingTs(currentPtr, lastPtr);
|
addIntersectingTs(currentPtr, lastPtr);
|
||||||
computeInterceptBottom(activeEdges, y, bottom);
|
computeInterceptBottom(activeEdges, y, bottom);
|
||||||
SkTDArray<ActiveEdge*> activeEdgeList;
|
SkTDArray<ActiveEdge*> activeEdgeList;
|
||||||
sortHorizontal(activeEdges, activeEdgeList, windingMask);
|
sortHorizontal(activeEdges, activeEdgeList, windingMask, y, bottom);
|
||||||
stitchEdge(activeEdgeList, y, bottom, windingMask, outBuilder);
|
stitchEdge(activeEdgeList, y, bottom, windingMask, outBuilder);
|
||||||
}
|
}
|
||||||
y = bottom;
|
y = bottom;
|
||||||
|
423
experimental/Intersection/EdgeWalkerPolygons_Test.cpp
Normal file
423
experimental/Intersection/EdgeWalkerPolygons_Test.cpp
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
#include "EdgeWalker_Test.h"
|
||||||
|
#include "Intersection_Tests.h"
|
||||||
|
|
||||||
|
static void testSimplifyTriangle() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kWinding_FillType);
|
||||||
|
path.moveTo(10,10); // triangle |\ .
|
||||||
|
path.lineTo(10,30); // |_\ .
|
||||||
|
path.lineTo(20,30);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(20,10); // triangle /|
|
||||||
|
path.lineTo(10,30); // /_|
|
||||||
|
path.lineTo(20,30);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out); // expect |\/|
|
||||||
|
comparePaths(path, out); // |__|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle3() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(3, 1);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle4() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(2, 1);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle5() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 1);
|
||||||
|
path.lineTo(2, 1);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle6() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(3, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle7() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 1);
|
||||||
|
path.lineTo(0, 2);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle8() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 1);
|
||||||
|
path.lineTo(1, 2);
|
||||||
|
path.lineTo(1, 3);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle9() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(1, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 1);
|
||||||
|
path.lineTo(2, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle10() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(1, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(2, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle11() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 2);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(2, 1);
|
||||||
|
path.lineTo(2, 2);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle12() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(1, 2);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(2, 0);
|
||||||
|
path.lineTo(0, 3);
|
||||||
|
path.lineTo(1, 1);
|
||||||
|
path.lineTo(2, 0);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle13() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 3);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(3, 0);
|
||||||
|
path.lineTo(0, 3);
|
||||||
|
path.lineTo(1, 1);
|
||||||
|
path.lineTo(3, 0);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle14() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(0, 0);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle15() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kEvenOdd_FillType);
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(1, 2);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(2, 2);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle16() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(1, 2);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(1, 3);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyTriangle17() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.lineTo(1, 2);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(1, 3);
|
||||||
|
path.lineTo(0, 1);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyWindingParallelogram() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kWinding_FillType);
|
||||||
|
path.moveTo(20,10); // parallelogram _
|
||||||
|
path.lineTo(30,30); // \ \ .
|
||||||
|
path.lineTo(40,30); // \_\ .
|
||||||
|
path.lineTo(30,10);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(20,10); // parallelogram _
|
||||||
|
path.lineTo(10,30); // / /
|
||||||
|
path.lineTo(20,30); // /_/
|
||||||
|
path.lineTo(30,10);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out); // expect _
|
||||||
|
comparePaths(path, out); // / \ .
|
||||||
|
} // /___\ .
|
||||||
|
|
||||||
|
static void testSimplifyXorParallelogram() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kEvenOdd_FillType);
|
||||||
|
path.moveTo(20,10); // parallelogram _
|
||||||
|
path.lineTo(30,30); // \ \ .
|
||||||
|
path.lineTo(40,30); // \_\ .
|
||||||
|
path.lineTo(30,10);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(20,10); // parallelogram _
|
||||||
|
path.lineTo(10,30); // / /
|
||||||
|
path.lineTo(20,30); // /_/
|
||||||
|
path.lineTo(30,10);
|
||||||
|
path.close();
|
||||||
|
simplify(path, true, out); // expect _
|
||||||
|
comparePaths(path, out); // \ /
|
||||||
|
} //
|
||||||
|
|
||||||
|
static void testSimplifyTriangle2() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kWinding_FillType);
|
||||||
|
path.moveTo(10,10); // triangle |\ .
|
||||||
|
path.lineTo(10,30); // |_\ .
|
||||||
|
path.lineTo(20,30);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(10,10); // triangle _
|
||||||
|
path.lineTo(20,10); // \ |
|
||||||
|
path.lineTo(20,30); // \|
|
||||||
|
path.close(); // _
|
||||||
|
simplify(path, true, out); // expect | |
|
||||||
|
comparePaths(path, out); // |_|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyNondegenerate4x4Triangles() {
|
||||||
|
char pathStr[1024];
|
||||||
|
bzero(pathStr, sizeof(pathStr));
|
||||||
|
for (int a = 0; a < 15; ++a) {
|
||||||
|
int ax = a & 0x03;
|
||||||
|
int ay = a >> 2;
|
||||||
|
for (int b = a + 1; b < 16; ++b) {
|
||||||
|
int bx = b & 0x03;
|
||||||
|
int by = b >> 2;
|
||||||
|
for (int c = a + 1; c < 16; ++c) {
|
||||||
|
if (b == c) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int cx = c & 0x03;
|
||||||
|
int cy = c >> 2;
|
||||||
|
if ((bx - ax) * (cy - ay) == (by - ay) * (cx - ax)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int d = 0; d < 15; ++d) {
|
||||||
|
int dx = d & 0x03;
|
||||||
|
int dy = d >> 2;
|
||||||
|
for (int e = d + 1; e < 16; ++e) {
|
||||||
|
int ex = e & 0x03;
|
||||||
|
int ey = e >> 2;
|
||||||
|
for (int f = d + 1; f < 16; ++f) {
|
||||||
|
if (e == f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int fx = f & 0x03;
|
||||||
|
int fy = f >> 2;
|
||||||
|
if ((ex - dx) * (fy - dy) == (ey - dy) * (fx - dx)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kWinding_FillType);
|
||||||
|
path.moveTo(ax, ay);
|
||||||
|
path.lineTo(bx, by);
|
||||||
|
path.lineTo(cx, cy);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(dx, dy);
|
||||||
|
path.lineTo(ex, ey);
|
||||||
|
path.lineTo(fx, fy);
|
||||||
|
path.close();
|
||||||
|
if (1) {
|
||||||
|
char* str = pathStr;
|
||||||
|
str += sprintf(str, " path.moveTo(%d, %d);\n", ax, ay);
|
||||||
|
str += sprintf(str, " path.lineTo(%d, %d);\n", bx, by);
|
||||||
|
str += sprintf(str, " path.lineTo(%d, %d);\n", cx, cy);
|
||||||
|
str += sprintf(str, " path.close();\n");
|
||||||
|
str += sprintf(str, " path.moveTo(%d, %d);\n", dx, dy);
|
||||||
|
str += sprintf(str, " path.lineTo(%d, %d);\n", ex, ey);
|
||||||
|
str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy);
|
||||||
|
str += sprintf(str, " path.close();");
|
||||||
|
}
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
path.setFillType(SkPath::kEvenOdd_FillType);
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testPathTriangleRendering() {
|
||||||
|
SkPath one, two;
|
||||||
|
one.moveTo(0, 0);
|
||||||
|
one.lineTo(3, 3);
|
||||||
|
one.lineTo(0, 3);
|
||||||
|
one.lineTo(1, 2);
|
||||||
|
one.close();
|
||||||
|
for (float x = .1f; x <= 2.9f; x += .1f) {
|
||||||
|
SkDebugf("%s x=%g\n", __FUNCTION__, x);
|
||||||
|
two.moveTo(0, 0);
|
||||||
|
two.lineTo(x, x);
|
||||||
|
two.lineTo(3, 3);
|
||||||
|
two.lineTo(0, 3);
|
||||||
|
two.lineTo(1, 2);
|
||||||
|
two.close();
|
||||||
|
comparePaths(one, two);
|
||||||
|
two.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void (*simplifyTests[])() = {
|
||||||
|
testSimplifyTriangle17,
|
||||||
|
testSimplifyTriangle16,
|
||||||
|
testSimplifyTriangle15,
|
||||||
|
testSimplifyTriangle14,
|
||||||
|
testSimplifyTriangle13,
|
||||||
|
testSimplifyTriangle12,
|
||||||
|
testSimplifyTriangle11,
|
||||||
|
testSimplifyTriangle10,
|
||||||
|
testSimplifyTriangle7,
|
||||||
|
testSimplifyTriangle9,
|
||||||
|
testSimplifyTriangle8,
|
||||||
|
testSimplifyTriangle6,
|
||||||
|
testSimplifyTriangle5,
|
||||||
|
testSimplifyTriangle4,
|
||||||
|
testSimplifyTriangle3,
|
||||||
|
testSimplifyTriangle,
|
||||||
|
testSimplifyTriangle2,
|
||||||
|
testSimplifyWindingParallelogram,
|
||||||
|
testSimplifyXorParallelogram,
|
||||||
|
testSimplifyNondegenerate4x4Triangles,
|
||||||
|
testPathTriangleRendering,
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
|
||||||
|
|
||||||
|
static void (*firstTest)() = 0;
|
||||||
|
|
||||||
|
void SimplifyPolygonPaths_Test() {
|
||||||
|
size_t index = 0;
|
||||||
|
if (firstTest) {
|
||||||
|
while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( ; index < simplifyTestsCount; ++index) {
|
||||||
|
(*simplifyTests[index])();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
452
experimental/Intersection/EdgeWalkerRectangles_Test.cpp
Normal file
452
experimental/Intersection/EdgeWalkerRectangles_Test.cpp
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
#include "EdgeWalker_Test.h"
|
||||||
|
#include "Intersection_Tests.h"
|
||||||
|
|
||||||
|
static void testSimplifyCoincidentVertical() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kWinding_FillType);
|
||||||
|
path.addRect(10, 10, 30, 30);
|
||||||
|
path.addRect(10, 30, 30, 40);
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkRect rect;
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != SkRect::MakeLTRB(10, 10, 30, 40)) {
|
||||||
|
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyCoincidentHorizontal() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kWinding_FillType);
|
||||||
|
path.addRect(10, 10, 30, 30);
|
||||||
|
path.addRect(30, 10, 40, 30);
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkRect rect;
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != SkRect::MakeLTRB(10, 10, 40, 30)) {
|
||||||
|
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyMulti() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kWinding_FillType);
|
||||||
|
path.addRect(10, 10, 30, 30);
|
||||||
|
path.addRect(20, 20, 40, 40);
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkPath expected;
|
||||||
|
expected.setFillType(SkPath::kEvenOdd_FillType);
|
||||||
|
expected.moveTo(10,10); // two cutout corners
|
||||||
|
expected.lineTo(10,30);
|
||||||
|
expected.lineTo(20,30);
|
||||||
|
expected.lineTo(20,40);
|
||||||
|
expected.lineTo(40,40);
|
||||||
|
expected.lineTo(40,20);
|
||||||
|
expected.lineTo(30,20);
|
||||||
|
expected.lineTo(30,10);
|
||||||
|
expected.lineTo(10,10);
|
||||||
|
expected.close();
|
||||||
|
if (out != expected) {
|
||||||
|
SkDebugf("%s expected equal\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
path = out;
|
||||||
|
path.addRect(30, 10, 40, 20);
|
||||||
|
path.addRect(10, 30, 20, 40);
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkRect rect;
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
|
||||||
|
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
path = out;
|
||||||
|
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
|
||||||
|
simplify(path, true, out);
|
||||||
|
if (!out.isEmpty()) {
|
||||||
|
SkDebugf("%s expected empty\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyAddL() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.moveTo(10,10); // 'L' shape
|
||||||
|
path.lineTo(10,40);
|
||||||
|
path.lineTo(40,40);
|
||||||
|
path.lineTo(40,20);
|
||||||
|
path.lineTo(30,20);
|
||||||
|
path.lineTo(30,10);
|
||||||
|
path.lineTo(10,10);
|
||||||
|
path.close();
|
||||||
|
path.addRect(30, 10, 40, 20); // missing notch of 'L'
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkRect rect;
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
|
||||||
|
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyCoincidentCCW() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
|
||||||
|
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkRect rect;
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
|
||||||
|
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyCoincidentCW() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
|
||||||
|
path.addRect(10, 10, 40, 40, SkPath::kCW_Direction);
|
||||||
|
simplify(path, true, out);
|
||||||
|
if (!out.isEmpty()) {
|
||||||
|
SkDebugf("%s expected empty\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyCorner() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.addRect(10, 10, 20, 20, SkPath::kCCW_Direction);
|
||||||
|
path.addRect(20, 20, 40, 40, SkPath::kCW_Direction);
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkTDArray<SkRect> boundsArray;
|
||||||
|
contourBounds(out, boundsArray);
|
||||||
|
if (boundsArray.count() != 2) {
|
||||||
|
SkDebugf("%s expected 2 contours\n", __FUNCTION__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SkRect one = SkRect::MakeLTRB(10, 10, 20, 20);
|
||||||
|
SkRect two = SkRect::MakeLTRB(20, 20, 40, 40);
|
||||||
|
if (boundsArray[0] != one && boundsArray[0] != two
|
||||||
|
|| boundsArray[1] != one && boundsArray[1] != two) {
|
||||||
|
SkDebugf("%s expected match\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyDiagonal() {
|
||||||
|
SkRect rect2 = SkRect::MakeXYWH(10, 10, 10, 10);
|
||||||
|
for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
|
||||||
|
for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
|
||||||
|
for (int x = 0; x <= 20; x += 20) {
|
||||||
|
for (int y = 0; y <= 20; y += 20) {
|
||||||
|
SkPath path, out;
|
||||||
|
SkRect rect1 = SkRect::MakeXYWH(x, y, 10, 10);
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkPath::Iter iter(out, false);
|
||||||
|
SkPoint pts[4], lastLine[2];
|
||||||
|
SkPath::Verb verb;
|
||||||
|
SkRect bounds[2];
|
||||||
|
bounds[0].setEmpty();
|
||||||
|
bounds[1].setEmpty();
|
||||||
|
SkRect* boundsPtr = bounds;
|
||||||
|
int count = 0, segments = 0;
|
||||||
|
bool lastLineSet = false;
|
||||||
|
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
||||||
|
switch (verb) {
|
||||||
|
case SkPath::kMove_Verb:
|
||||||
|
if (!boundsPtr->isEmpty()) {
|
||||||
|
SkASSERT(boundsPtr == bounds);
|
||||||
|
++boundsPtr;
|
||||||
|
}
|
||||||
|
boundsPtr->set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
|
||||||
|
count = 0;
|
||||||
|
lastLineSet = false;
|
||||||
|
break;
|
||||||
|
case SkPath::kLine_Verb:
|
||||||
|
if (lastLineSet) {
|
||||||
|
SkASSERT((lastLine[1].fX - lastLine[0].fX) *
|
||||||
|
(pts[1].fY - lastLine[0].fY) !=
|
||||||
|
(lastLine[1].fY - lastLine[0].fY) *
|
||||||
|
(pts[1].fX - lastLine[0].fX));
|
||||||
|
}
|
||||||
|
lastLineSet = true;
|
||||||
|
lastLine[0] = pts[0];
|
||||||
|
lastLine[1] = pts[1];
|
||||||
|
count = 1;
|
||||||
|
++segments;
|
||||||
|
break;
|
||||||
|
case SkPath::kClose_Verb:
|
||||||
|
count = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SkDEBUGFAIL("bad verb");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 1; i <= count; ++i) {
|
||||||
|
boundsPtr->growToInclude(pts[i].fX, pts[i].fY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (boundsPtr != bounds) {
|
||||||
|
SkASSERT((bounds[0] == rect1 || bounds[1] == rect1)
|
||||||
|
&& (bounds[0] == rect2 || bounds[1] == rect2));
|
||||||
|
} else {
|
||||||
|
SkASSERT(segments == 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assertOneContour(const SkPath& out, bool edge, bool extend) {
|
||||||
|
SkPath::Iter iter(out, false);
|
||||||
|
SkPoint pts[4];
|
||||||
|
SkPath::Verb verb;
|
||||||
|
SkRect bounds;
|
||||||
|
bounds.setEmpty();
|
||||||
|
int count = 0;
|
||||||
|
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
||||||
|
switch (verb) {
|
||||||
|
case SkPath::kMove_Verb:
|
||||||
|
SkASSERT(count == 0);
|
||||||
|
break;
|
||||||
|
case SkPath::kLine_Verb:
|
||||||
|
SkASSERT(pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY);
|
||||||
|
++count;
|
||||||
|
break;
|
||||||
|
case SkPath::kClose_Verb:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SkDEBUGFAIL("bad verb");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SkASSERT(count == (extend ? 4 : edge ? 6 : 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyCoincident() {
|
||||||
|
// outside to inside, outside to right, outside to outside
|
||||||
|
// left to inside, left to right, left to outside
|
||||||
|
// inside to right, inside to outside
|
||||||
|
// repeat above for left, right, bottom
|
||||||
|
SkScalar start[] = { 0, 10, 20 };
|
||||||
|
size_t startCount = sizeof(start) / sizeof(start[0]);
|
||||||
|
SkScalar stop[] = { 30, 40, 50 };
|
||||||
|
size_t stopCount = sizeof(stop) / sizeof(stop[0]);
|
||||||
|
SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
|
||||||
|
for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
|
||||||
|
for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
|
||||||
|
for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
|
||||||
|
for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
|
||||||
|
bool extend = start[startIndex] == rect2.fLeft && stop[stopIndex] == rect2.fRight;
|
||||||
|
bool edge = start[startIndex] == rect2.fLeft || stop[stopIndex] == rect2.fRight;
|
||||||
|
SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 10);
|
||||||
|
SkPath path, out;
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
assertOneContour(out, edge, extend);
|
||||||
|
|
||||||
|
path.reset();
|
||||||
|
rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 50);
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
assertOneContour(out, edge, extend);
|
||||||
|
|
||||||
|
path.reset();
|
||||||
|
rect1 = SkRect::MakeLTRB(0, start[startIndex], 10, stop[stopIndex]);
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
assertOneContour(out, edge, extend);
|
||||||
|
|
||||||
|
path.reset();
|
||||||
|
rect1 = SkRect::MakeLTRB(40, start[startIndex], 50, stop[stopIndex]);
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
assertOneContour(out, edge, extend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyOverlap() {
|
||||||
|
SkScalar start[] = { 0, 10, 20 };
|
||||||
|
size_t startCount = sizeof(start) / sizeof(start[0]);
|
||||||
|
SkScalar stop[] = { 30, 40, 50 };
|
||||||
|
size_t stopCount = sizeof(stop) / sizeof(stop[0]);
|
||||||
|
SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
|
||||||
|
for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
|
||||||
|
for (size_t lefty = 0; lefty < startCount; ++lefty) {
|
||||||
|
for (size_t righty = 0; righty < stopCount; ++righty) {
|
||||||
|
for (size_t toppy = 0; toppy < startCount; ++toppy) {
|
||||||
|
for (size_t botty = 0; botty < stopCount; ++botty) {
|
||||||
|
SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
|
||||||
|
stop[righty], stop[botty]);
|
||||||
|
SkPath path, out;
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(dir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(dir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePaths(path, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyOverlapTiny() {
|
||||||
|
SkScalar start[] = { 0, 1, 2 };
|
||||||
|
size_t startCount = sizeof(start) / sizeof(start[0]);
|
||||||
|
SkScalar stop[] = { 3, 4, 5 };
|
||||||
|
size_t stopCount = sizeof(stop) / sizeof(stop[0]);
|
||||||
|
SkRect rect2 = SkRect::MakeXYWH(1, 1, 3, 3);
|
||||||
|
for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
|
||||||
|
for (size_t lefty = 0; lefty < startCount; ++lefty) {
|
||||||
|
for (size_t righty = 0; righty < stopCount; ++righty) {
|
||||||
|
for (size_t toppy = 0; toppy < startCount; ++toppy) {
|
||||||
|
for (size_t botty = 0; botty < stopCount; ++botty) {
|
||||||
|
SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
|
||||||
|
stop[righty], stop[botty]);
|
||||||
|
SkPath path, out;
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(dir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(dir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
comparePathsTiny(path, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyDegenerate() {
|
||||||
|
SkScalar start[] = { 0, 10, 20 };
|
||||||
|
size_t startCount = sizeof(start) / sizeof(start[0]);
|
||||||
|
SkScalar stop[] = { 30, 40, 50 };
|
||||||
|
size_t stopCount = sizeof(stop) / sizeof(stop[0]);
|
||||||
|
SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
|
||||||
|
for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
|
||||||
|
for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
|
||||||
|
for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
|
||||||
|
for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
|
||||||
|
SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 0);
|
||||||
|
SkPath path, out;
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkRect rect;
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s 1 expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != rect2) {
|
||||||
|
SkDebugf("%s 1 expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.reset();
|
||||||
|
rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 40);
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s 2 expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != rect2) {
|
||||||
|
SkDebugf("%s 2 expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.reset();
|
||||||
|
rect1 = SkRect::MakeLTRB(0, start[startIndex], 0, stop[stopIndex]);
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s 3 expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != rect2) {
|
||||||
|
SkDebugf("%s 3 expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.reset();
|
||||||
|
rect1 = SkRect::MakeLTRB(40, start[startIndex], 40, stop[stopIndex]);
|
||||||
|
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
|
||||||
|
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
|
||||||
|
simplify(path, true, out);
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s 4 expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != rect2) {
|
||||||
|
SkDebugf("%s 4 expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSimplifyDegenerate1() {
|
||||||
|
SkPath path, out;
|
||||||
|
path.setFillType(SkPath::kWinding_FillType);
|
||||||
|
path.addRect( 0, 0, 0, 30);
|
||||||
|
path.addRect(10, 10, 40, 40);
|
||||||
|
simplify(path, true, out);
|
||||||
|
SkRect rect;
|
||||||
|
if (!out.isRect(&rect)) {
|
||||||
|
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
|
||||||
|
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void (*simplifyTests[])() = {
|
||||||
|
testSimplifyOverlapTiny,
|
||||||
|
testSimplifyDegenerate1,
|
||||||
|
testSimplifyCorner,
|
||||||
|
testSimplifyDegenerate,
|
||||||
|
testSimplifyOverlap,
|
||||||
|
testSimplifyDiagonal,
|
||||||
|
testSimplifyCoincident,
|
||||||
|
testSimplifyCoincidentCW,
|
||||||
|
testSimplifyCoincidentCCW,
|
||||||
|
testSimplifyCoincidentVertical,
|
||||||
|
testSimplifyCoincidentHorizontal,
|
||||||
|
testSimplifyAddL,
|
||||||
|
testSimplifyMulti,
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
|
||||||
|
|
||||||
|
static void (*firstTest)() = 0;
|
||||||
|
|
||||||
|
void SimplifyRectangularPaths_Test() {
|
||||||
|
size_t index = 0;
|
||||||
|
if (firstTest) {
|
||||||
|
while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( ; index < simplifyTestsCount; ++index) {
|
||||||
|
if (simplifyTests[index] == testSimplifyCorner) {
|
||||||
|
// testSimplifyCorner fails because it expects two contours, where
|
||||||
|
// only one is returned. Both results are reasonable, but if two
|
||||||
|
// contours are desirable, or if we provide an option to choose
|
||||||
|
// between longer contours and more contours, turn this back on. For
|
||||||
|
// the moment, testSimplifyDiagonal also checks the test case, and
|
||||||
|
// permits either two rects or one non-crossing poly as valid
|
||||||
|
// unreported results.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(*simplifyTests[index])();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
9
experimental/Intersection/EdgeWalker_Test.h
Normal file
9
experimental/Intersection/EdgeWalker_Test.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include "SkPath.h"
|
||||||
|
|
||||||
|
extern void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray);
|
||||||
|
extern void comparePaths(const SkPath& one, const SkPath& two);
|
||||||
|
extern void comparePathsTiny(const SkPath& one, const SkPath& two);
|
||||||
|
extern void simplify(const SkPath& path, bool asFill, SkPath& simple);
|
||||||
|
|
190
experimental/Intersection/EdgeWalker_TestUtility.cpp
Normal file
190
experimental/Intersection/EdgeWalker_TestUtility.cpp
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
#include "EdgeWalker_Test.h"
|
||||||
|
#include "Intersection_Tests.h"
|
||||||
|
#include "SkBitmap.h"
|
||||||
|
#include "SkCanvas.h"
|
||||||
|
#include "SkPaint.h"
|
||||||
|
|
||||||
|
static bool gDrawLastAsciiPaths = true;
|
||||||
|
static bool gDrawAllAsciiPaths = false;
|
||||||
|
static bool gShowPath = true;
|
||||||
|
|
||||||
|
static void showPath(const char* str, const SkPath& path) {
|
||||||
|
if (!gShowPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SkDebugf("%s\n", str);
|
||||||
|
SkPath::Iter iter(path, true);
|
||||||
|
uint8_t verb;
|
||||||
|
SkPoint pts[4];
|
||||||
|
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
||||||
|
switch (verb) {
|
||||||
|
case SkPath::kMove_Verb:
|
||||||
|
SkDebugf("path.moveTo(%g, %g);\n", pts[0].fX, pts[0].fY);
|
||||||
|
continue;
|
||||||
|
case SkPath::kLine_Verb:
|
||||||
|
SkDebugf("path.lineTo(%g, %g);\n", pts[1].fX, pts[1].fY);
|
||||||
|
break;
|
||||||
|
case SkPath::kQuad_Verb:
|
||||||
|
SkDebugf("path.quadTo(%g, %g, %g, %g);\n",
|
||||||
|
pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
|
||||||
|
break;
|
||||||
|
case SkPath::kCubic_Verb:
|
||||||
|
SkDebugf("path.cubicTo(%g, %g, %g, %g);\n",
|
||||||
|
pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
|
||||||
|
pts[3].fX, pts[3].fY);
|
||||||
|
break;
|
||||||
|
case SkPath::kClose_Verb:
|
||||||
|
SkDebugf("path.close();\n");
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
SkDEBUGFAIL("bad verb");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pathsDrawTheSame(const SkPath& one, const SkPath& two) {
|
||||||
|
const SkRect& bounds1 = one.getBounds();
|
||||||
|
const SkRect& bounds2 = two.getBounds();
|
||||||
|
SkRect larger = bounds1;
|
||||||
|
larger.join(bounds2);
|
||||||
|
SkBitmap bits;
|
||||||
|
int bitWidth = SkScalarCeil(larger.width()) + 2;
|
||||||
|
int bitHeight = SkScalarCeil(larger.height()) + 2;
|
||||||
|
bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
|
||||||
|
bits.allocPixels();
|
||||||
|
SkCanvas canvas(bits);
|
||||||
|
canvas.drawColor(SK_ColorWHITE);
|
||||||
|
SkPaint paint;
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
|
||||||
|
canvas.drawPath(one, paint);
|
||||||
|
canvas.restore();
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
|
||||||
|
canvas.drawPath(two, paint);
|
||||||
|
canvas.restore();
|
||||||
|
for (int y = 0; y < bitHeight; ++y) {
|
||||||
|
uint32_t* addr1 = bits.getAddr32(0, y);
|
||||||
|
uint32_t* addr2 = bits.getAddr32(bitWidth, y);
|
||||||
|
for (int x = 0; x < bitWidth; ++x) {
|
||||||
|
if (addr1[x] != addr2[x]) {
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawAsciiPaths(const SkPath& one, const SkPath& two,
|
||||||
|
bool drawPaths) {
|
||||||
|
if (!drawPaths) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (0) {
|
||||||
|
showPath("one:", one);
|
||||||
|
showPath("two:", two);
|
||||||
|
}
|
||||||
|
const SkRect& bounds1 = one.getBounds();
|
||||||
|
const SkRect& bounds2 = two.getBounds();
|
||||||
|
SkRect larger = bounds1;
|
||||||
|
larger.join(bounds2);
|
||||||
|
SkBitmap bits;
|
||||||
|
int bitWidth = SkScalarCeil(larger.width()) + 2;
|
||||||
|
int bitHeight = SkScalarCeil(larger.height()) + 2;
|
||||||
|
bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
|
||||||
|
bits.allocPixels();
|
||||||
|
SkCanvas canvas(bits);
|
||||||
|
canvas.drawColor(SK_ColorWHITE);
|
||||||
|
SkPaint paint;
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
|
||||||
|
canvas.drawPath(one, paint);
|
||||||
|
canvas.restore();
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(-bounds2.fLeft + 1 + bitWidth, -bounds2.fTop + 1);
|
||||||
|
canvas.drawPath(two, paint);
|
||||||
|
canvas.restore();
|
||||||
|
char out[1024];
|
||||||
|
SkASSERT(bitWidth * 2 + 1 < (int) sizeof(out));
|
||||||
|
for (int y = 0; y < bitHeight; ++y) {
|
||||||
|
uint32_t* addr1 = bits.getAddr32(0, y);
|
||||||
|
int x;
|
||||||
|
char* outPtr = out;
|
||||||
|
for (x = 0; x < bitWidth; ++x) {
|
||||||
|
*outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
|
||||||
|
}
|
||||||
|
*outPtr++ = '|';
|
||||||
|
for (x = bitWidth; x < bitWidth * 2; ++x) {
|
||||||
|
*outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
|
||||||
|
}
|
||||||
|
*outPtr++ = '\0';
|
||||||
|
SkDebugf("%s\n", out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool scaledDrawTheSame(const SkPath& one, const SkPath& two,
|
||||||
|
int a, int b, bool drawPaths) {
|
||||||
|
SkMatrix scale;
|
||||||
|
scale.reset();
|
||||||
|
scale.preScale(a * 1.21f, b * 1.11f);
|
||||||
|
SkPath scaledOne, scaledTwo;
|
||||||
|
one.transform(scale, &scaledOne);
|
||||||
|
two.transform(scale, &scaledTwo);
|
||||||
|
if (pathsDrawTheSame(scaledOne, scaledTwo)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
drawAsciiPaths(scaledOne, scaledTwo, drawPaths);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void comparePaths(const SkPath& one, const SkPath& two) {
|
||||||
|
if (pathsDrawTheSame(one, two)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
drawAsciiPaths(one, two, gDrawAllAsciiPaths);
|
||||||
|
for (int x = 9; x <= 33; ++x) {
|
||||||
|
if (scaledDrawTheSame(one, two, x, x - (x >> 2), gDrawAllAsciiPaths)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!gDrawAllAsciiPaths) {
|
||||||
|
scaledDrawTheSame(one, two, 9, 7, gDrawLastAsciiPaths);
|
||||||
|
}
|
||||||
|
showPath("original:", one);
|
||||||
|
showPath("simplified:", two);
|
||||||
|
SkASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// doesn't work yet
|
||||||
|
void comparePathsTiny(const SkPath& one, const SkPath& two) {
|
||||||
|
const SkRect& bounds1 = one.getBounds();
|
||||||
|
const SkRect& bounds2 = two.getBounds();
|
||||||
|
SkRect larger = bounds1;
|
||||||
|
larger.join(bounds2);
|
||||||
|
SkBitmap bits;
|
||||||
|
int bitWidth = SkScalarCeil(larger.width()) + 2;
|
||||||
|
int bitHeight = SkScalarCeil(larger.height()) + 2;
|
||||||
|
bits.setConfig(SkBitmap::kA1_Config, bitWidth * 2, bitHeight);
|
||||||
|
bits.allocPixels();
|
||||||
|
SkCanvas canvas(bits);
|
||||||
|
canvas.drawColor(SK_ColorWHITE);
|
||||||
|
SkPaint paint;
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
|
||||||
|
canvas.drawPath(one, paint);
|
||||||
|
canvas.restore();
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(-bounds2.fLeft + 1, -bounds2.fTop + 1);
|
||||||
|
canvas.drawPath(two, paint);
|
||||||
|
canvas.restore();
|
||||||
|
for (int y = 0; y < bitHeight; ++y) {
|
||||||
|
uint8_t* addr1 = bits.getAddr1(0, y);
|
||||||
|
uint8_t* addr2 = bits.getAddr1(bitWidth, y);
|
||||||
|
for (int x = 0; x < bits.rowBytes(); ++x) {
|
||||||
|
SkASSERT(addr1[x] == addr2[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,40 +3,32 @@
|
|||||||
// FIXME: Move this templated version into SKTSearch.h
|
// FIXME: Move this templated version into SKTSearch.h
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void QSort_Partition(T** first, T** last)
|
static T** QSort_Partition(T** left, T** right, T** pivot)
|
||||||
{
|
{
|
||||||
T** left = first;
|
T* pivotValue = *pivot;
|
||||||
T** rite = last;
|
SkTSwap(*pivot, *right);
|
||||||
T** pivot = left;
|
T** newPivot = left;
|
||||||
|
while (left < right) {
|
||||||
while (left <= rite) {
|
if (**left < *pivotValue) {
|
||||||
while (left < last && **left < **pivot)
|
SkTSwap(*left, *newPivot);
|
||||||
left += 1;
|
newPivot += 1;
|
||||||
while (first < rite && **pivot < **rite)
|
|
||||||
rite -= 1;
|
|
||||||
if (left <= rite) {
|
|
||||||
if (left < rite) {
|
|
||||||
SkTSwap(*left, *rite);
|
|
||||||
}
|
|
||||||
left += 1;
|
|
||||||
rite -= 1;
|
|
||||||
}
|
}
|
||||||
|
left += 1;
|
||||||
}
|
}
|
||||||
if (first < rite)
|
SkTSwap(*newPivot, *right);
|
||||||
QSort_Partition(first, rite);
|
return newPivot;
|
||||||
if (left < last)
|
|
||||||
QSort_Partition(left, last);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void QSort(T** base, size_t count)
|
void QSort(T** left, T** right)
|
||||||
{
|
{
|
||||||
SkASSERT(base);
|
if (left >= right) {
|
||||||
|
|
||||||
if (count <= 1) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QSort_Partition(base, base + (count - 1));
|
T** pivot = left + (right - left >> 1);
|
||||||
|
pivot = QSort_Partition(left, right, pivot);
|
||||||
|
QSort(left, pivot - 1);
|
||||||
|
QSort(pivot + 1, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename T>
|
template <typename S, typename T>
|
||||||
|
Loading…
Reference in New Issue
Block a user