shape ops work in progress

git-svn-id: http://skia.googlecode.com/svn/trunk@3566 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
caryclark@google.com 2012-03-30 18:47:02 +00:00
parent 06cd732eff
commit 198e054b33
19 changed files with 547 additions and 369 deletions

View File

@ -1,40 +0,0 @@
#ifndef CubicIntersection_DEFINE
#define CubicIntersection_DEFINE
#include "DataTypes.h"
class Intersections;
// unit-testable utilities
bool bezier_clip(const Cubic& cubic1, const Cubic& cubic2, double& minT, double& maxT);
bool bezier_clip(const Quadratic& q1, const Quadratic& q2, double& minT, double& maxT);
void chop_at(const Cubic& src, CubicPair& dst, double t);
void chop_at(const Quadratic& src, QuadraticPair& dst, double t);
int convex_hull(const Cubic& cubic, char order[4]);
bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]);
bool implicit_matches(const Cubic& cubic1, const Cubic& cubic2);
bool implicit_matches(const Quadratic& quad1, const Quadratic& quad2);
void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst);
void sub_divide(const Quadratic& src, double t1, double t2, Quadratic& dst);
void tangent(const Cubic& cubic, double t, _Point& result);
void tangent(const Quadratic& cubic, double t, _Point& result);
// utilities used only for unit testing
bool point_on_parameterized_curve(const Cubic& cubic, const _Point& point);
bool point_on_parameterized_curve(const Quadratic& quad, const _Point& point);
// main functions
enum ReduceOrder_Flags {
kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_QuadraticsAllowed
};
int reduceOrder(const Cubic& cubic, Cubic& reduction, ReduceOrder_Flags );
int reduceOrder(const _Line& line, _Line& reduction);
int reduceOrder(const Quadratic& quad, Quadratic& reduction);
//bool intersectStart(const Cubic& cubic1, const Cubic& cubic2, Intersections& );
bool intersectStartT(const Cubic& cubic1, const Cubic& cubic2, Intersections& );
bool intersectStart(const Cubic& cubic, const _Line& line, Intersections& );
bool intersectStart(const Quadratic& q1, const Quadratic& q2, Intersections& );
bool intersectStart(const Quadratic& quad, const _Line& line, Intersections& );
#endif

View File

@ -31,6 +31,10 @@ int reduceOrder(const Cubic& cubic, Cubic& reduction, ReduceOrder_Flags );
int reduceOrder(const _Line& line, _Line& reduction); int reduceOrder(const _Line& line, _Line& reduction);
int reduceOrder(const Quadratic& quad, Quadratic& reduction); int reduceOrder(const Quadratic& quad, Quadratic& reduction);
int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]); int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]);
int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
double tRange[3]);
int horizontalIntersect(const Quadratic& quad, double left, double right,
double y, double tRange[2]);
bool intersect(const Cubic& cubic1, const Cubic& cubic2, Intersections& ); bool intersect(const Cubic& cubic1, const Cubic& cubic2, Intersections& );
int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]); int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]);
bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& ); bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& );

View File

@ -16,7 +16,7 @@
#include "ShapeOps.h" #include "ShapeOps.h"
#include "TSearch.h" #include "TSearch.h"
#if 01 // set to 1 for no debugging whatsoever #if 0 // set to 1 for no debugging whatsoever
const bool gShowDebugf = false; // FIXME: remove once debugging is complete const bool gShowDebugf = false; // FIXME: remove once debugging is complete
#define DEBUG_DUMP 0 #define DEBUG_DUMP 0
@ -30,6 +30,7 @@ const bool gShowDebugf = false; // FIXME: remove once debugging is complete
#define DEBUG_OUT_LESS_THAN 0 #define DEBUG_OUT_LESS_THAN 0
#define DEBUG_ADJUST_COINCIDENT 0 #define DEBUG_ADJUST_COINCIDENT 0
#define DEBUG_BOTTOM 0 #define DEBUG_BOTTOM 0
#define DEBUG_SPLIT 0
#else #else
const bool gShowDebugf = true; // FIXME: remove once debugging is complete const bool gShowDebugf = true; // FIXME: remove once debugging is complete
@ -44,7 +45,8 @@ const bool gShowDebugf = true; // FIXME: remove once debugging is complete
#define DEBUG_OUT 01 #define DEBUG_OUT 01
#define DEBUG_OUT_LESS_THAN 0 #define DEBUG_OUT_LESS_THAN 0
#define DEBUG_ADJUST_COINCIDENT 1 #define DEBUG_ADJUST_COINCIDENT 1
#define DEBUG_BOTTOM 01 #define DEBUG_BOTTOM 0
#define DEBUG_SPLIT 1
#endif #endif
@ -59,7 +61,8 @@ static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
Intersections& intersections) { Intersections& intersections) {
const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}}; const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}}; const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
return intersect(aQuad, bLine, intersections); intersect(aQuad, bLine, intersections);
return intersections.fUsed;
} }
static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3], static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
@ -67,15 +70,15 @@ static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}, const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
{a[3].fX, a[3].fY}}; {a[3].fX, a[3].fY}};
const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}}; const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
intersections.fUsed = intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]); return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
return intersections.fUsed;
} }
static int QuadIntersect(const SkPoint a[3], const SkPoint b[3], static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
Intersections& intersections) { Intersections& intersections) {
const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}}; const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}}; const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
return intersect(aQuad, bQuad, intersections); intersect(aQuad, bQuad, intersections);
return intersections.fUsed;
} }
static int CubicIntersect(const SkPoint a[4], const SkPoint b[4], static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
@ -84,7 +87,8 @@ static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
{a[3].fX, a[3].fY}}; {a[3].fX, a[3].fY}};
const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}, const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
{b[3].fX, b[3].fY}}; {b[3].fX, b[3].fY}};
return intersect(aCubic, bCubic, intersections); intersect(aCubic, bCubic, intersections);
return intersections.fUsed;
} }
static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right, static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
@ -93,6 +97,19 @@ static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
return horizontalLineIntersect(aLine, left, right, y, aRange); return horizontalLineIntersect(aLine, left, right, y, aRange);
} }
static int QuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
SkScalar y, double aRange[3]) {
const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
return horizontalIntersect(aQuad, left, right, y, aRange);
}
static int CubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
SkScalar y, double aRange[4]) {
const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
{a[3].fX, a[3].fY}};
return horizontalIntersect(aCubic, left, right, y, aRange);
}
static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) { static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}}; const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
double x, y; double x, y;
@ -632,7 +649,25 @@ class Intercepts {
public: public:
Intercepts() Intercepts()
: fTopIntercepts(0) : fTopIntercepts(0)
, fBottomIntercepts(0) { , fBottomIntercepts(0)
, fExplicit(false) {
}
Intercepts& operator=(const Intercepts& src) {
fTs = src.fTs;
fTopIntercepts = src.fTopIntercepts;
fBottomIntercepts = src.fBottomIntercepts;
}
// OPTIMIZATION: remove this function if it's never called
double t(int tIndex) const {
if (tIndex == 0) {
return 0;
}
if (tIndex > fTs.count()) {
return 1;
}
return fTs[tIndex - 1];
} }
#if DEBUG_DUMP #if DEBUG_DUMP
@ -657,16 +692,18 @@ public:
SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className), SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className),
className, i, fTs[i], out.fX, out.fY); className, i, fTs[i], out.fX, out.fY);
} }
SkDebugf("%*s.fTopIntercepts=%d\n", tab + sizeof(className), SkDebugf("%*s.fTopIntercepts=%u\n", tab + sizeof(className),
className, fTopIntercepts); className, fTopIntercepts);
SkDebugf("%*s.fBottomIntercepts=%d\n", tab + sizeof(className), SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className),
className, fBottomIntercepts); className, fBottomIntercepts);
} }
#endif #endif
SkTDArray<double> fTs; SkTDArray<double> fTs;
int fTopIntercepts; unsigned char fTopIntercepts; // 0=init state 1=1 edge >1=multiple edges
int fBottomIntercepts; unsigned char fBottomIntercepts;
bool fExplicit; // if set, suppress 0 and 1
}; };
struct HorizontalEdge { struct HorizontalEdge {
@ -709,13 +746,16 @@ struct InEdge {
for (size_t index = 0; index < count; ++index) { for (size_t index = 0; index < count; ++index) {
double t = ts[index]; double t = ts[index];
if (t <= 0) { if (t <= 0) {
intercepts.fTopIntercepts <<= 1;
fContainsIntercepts |= ++intercepts.fTopIntercepts > 1; fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
continue; continue;
} }
if (t >= 1) { if (t >= 1) {
intercepts.fBottomIntercepts <<= 1;
fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1; fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
continue; continue;
} }
fIntersected = true;
foundIntercept = true; foundIntercept = true;
size_t tCount = intercepts.fTs.count(); size_t tCount = intercepts.fTs.count();
double delta; double delta;
@ -740,6 +780,40 @@ struct InEdge {
fContainsIntercepts |= foundIntercept; fContainsIntercepts |= foundIntercept;
return insertedAt; return insertedAt;
} }
void addPartial(SkTArray<InEdge>& edges, int id, int ptStart, int ptEnd,
int verbStart, int verbEnd) {
InEdge* edge = edges.push_back_n(1);
int verbCount = verbEnd - verbStart;
edge->fIntercepts.push_back_n(verbCount);
uint8_t* verbs = &fVerbs[verbStart];
for (int ceptIdx = 0; ceptIdx < verbCount; ++ceptIdx) {
edge->fIntercepts[ceptIdx] = fIntercepts[verbStart + ceptIdx];
}
edge->fPts.append(ptEnd - ptStart, &fPts[ptStart]);
edge->fVerbs.append(verbCount, &fVerbs[verbStart]);
edge->setBounds();
edge->fID = id + edges.count();
edge->fWinding = fWinding;
edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
}
void addSplit(SkTArray<InEdge>& edges, int id, SkPoint* pts, uint8_t verb,
double* ts, int tCount, bool flipped) {
InEdge* edge = edges.push_back_n(1);
edge->fIntercepts.push_back_n(1);
edge->fIntercepts[0].fTs.append(tCount, ts);
edge->fIntercepts[0].fExplicit = true;
edge->fPts.append(verb, pts);
edge->fVerbs.append(1, &verb);
edge->setBounds();
edge->fID = id + edges.count();
edge->fWinding = fWinding;
edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
if (flipped) {
flip();
}
}
bool cached(const InEdge* edge) { bool cached(const InEdge* edge) {
// FIXME: in the pathological case where there is a ton of edges, binary search? // FIXME: in the pathological case where there is a ton of edges, binary search?
@ -758,10 +832,44 @@ struct InEdge {
} }
void complete(signed char winding, int id) { void complete(signed char winding, int id) {
setBounds();
fIntercepts.push_back_n(fVerbs.count());
if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
flip();
}
fContainsIntercepts = fIntersected = false;
fID = id;
}
void flip() {
size_t index;
size_t last = fPts.count() - 1;
for (index = 0; index < last; ++index, --last) {
SkTSwap<SkPoint>(fPts[index], fPts[last]);
}
last = fVerbs.count() - 1;
for (index = 0; index < last; ++index, --last) {
SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
}
}
void reset() {
fCached.reset();
fIntercepts.reset();
fPts.reset();
fVerbs.reset();
fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
fID = -1;
fWinding = 0;
fContainsIntercepts = false;
fIntersected = false;
}
void setBounds() {
SkPoint* ptPtr = fPts.begin(); SkPoint* ptPtr = fPts.begin();
SkPoint* ptLast = fPts.end(); SkPoint* ptLast = fPts.end();
if (ptPtr == ptLast) { if (ptPtr == ptLast) {
SkDebugf("empty edge\n"); SkDebugf("%s empty edge\n", __FUNCTION__);
SkASSERT(0); SkASSERT(0);
// FIXME: delete empty edge? // FIXME: delete empty edge?
return; return;
@ -772,20 +880,113 @@ struct InEdge {
fBounds.growToInclude(ptPtr->fX, ptPtr->fY); fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
++ptPtr; ++ptPtr;
} }
fIntercepts.push_back_n(fVerbs.count()); }
if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
size_t index; void splitInflectionPts(SkTArray<InEdge>& edges, int idStart) {
size_t last = fPts.count() - 1; if (!fIntersected) {
for (index = 0; index < last; ++index, --last) { return;
SkTSwap<SkPoint>(fPts[index], fPts[last]); }
uint8_t* verbs = fVerbs.begin();
SkPoint* pts = fPts.begin();
int lastVerb = 0;
int lastPt = 0;
uint8_t verb;
bool edgeSplit = false;
for (int ceptIdx = 0; ceptIdx < fIntercepts.count(); ++ceptIdx, pts += verb) {
Intercepts& intercepts = fIntercepts[ceptIdx];
verb = *verbs++;
if (verb <= SkPath::kLine_Verb) {
continue;
} }
last = fVerbs.count() - 1; size_t tCount = intercepts.fTs.count();
for (index = 0; index < last; ++index, --last) { if (!tCount) {
SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]); continue;
}
size_t tIndex = -1;
SkScalar y = pts[0].fY;
int lastSplit = 0;
int firstSplit = -1;
bool curveSplit = false;
while (++tIndex < tCount) {
double nextT = intercepts.fTs[tIndex];
SkScalar nextY = verb == SkPath::kQuad_Verb
? QuadYAtT(pts, nextT) : CubicYAtT(pts, nextT);
if (nextY < y) {
edgeSplit = curveSplit = true;
if (firstSplit < 0) {
firstSplit = tIndex;
int nextPt = pts - fPts.begin();
int nextVerb = verbs - 1 - fVerbs.begin();
if (lastVerb < nextVerb) {
addPartial(edges, idStart, lastPt, nextPt,
lastVerb, nextVerb);
#if DEBUG_SPLIT
SkDebugf("%s addPartial 1 edge=%d\n", __FUNCTION__,
fID);
#endif
}
lastPt = nextPt;
lastVerb = nextVerb;
}
} else {
if (firstSplit >= 0) {
if (lastSplit < firstSplit) {
addSplit(edges, idStart, pts, verb,
&intercepts.fTs[lastSplit],
firstSplit - lastSplit, false);
#if DEBUG_SPLIT
SkDebugf("%s addSplit 1 edge=%d tIndex=%d,%d\n",
__FUNCTION__, fID, lastSplit, firstSplit);
#endif
}
addSplit(edges, idStart, pts, verb,
&intercepts.fTs[firstSplit],
tIndex - firstSplit, true);
#if DEBUG_SPLIT
SkDebugf("%s addSplit 2 edge=%d tIndex=%d,%d flip\n",
__FUNCTION__, fID, firstSplit, tIndex);
#endif
lastSplit = tIndex;
firstSplit = -1;
}
}
y = nextY;
}
if (curveSplit) {
if (firstSplit < 0) {
firstSplit = lastSplit;
} else {
addSplit(edges, idStart, pts, verb,
&intercepts.fTs[lastSplit], firstSplit - lastSplit,
false);
#if DEBUG_SPLIT
SkDebugf("%s addSplit 3 edge=%d tIndex=%d,%d\n", __FUNCTION__,
fID, lastSplit, firstSplit);
#endif
}
addSplit(edges, idStart, pts, verb,
&intercepts.fTs[firstSplit], tIndex - firstSplit,
pts[verb].fY < y);
#if DEBUG_SPLIT
SkDebugf("%s addSplit 4 edge=%d tIndex=%d,%d %s\n", __FUNCTION__,
fID, firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
#endif
} }
} }
fContainsIntercepts = false; // collapse remainder -- if there's nothing left, clear it somehow?
fID = id; if (edgeSplit) {
int nextVerb = verbs - 1 - fVerbs.begin();
if (lastVerb < nextVerb) {
int nextPt = pts - fPts.begin();
addPartial(edges, idStart, lastPt, nextPt,
lastVerb, nextVerb);
#if DEBUG_SPLIT
SkDebugf("%s addPartial 2 edge=%d\n", __FUNCTION__, fID);
#endif
}
// OPTIMIZATION: reuse the edge instead of marking it empty
reset();
}
} }
#if DEBUG_DUMP #if DEBUG_DUMP
@ -803,7 +1004,6 @@ struct InEdge {
for (i = 0; i < fIntercepts.count(); ++i) { for (i = 0; i < fIntercepts.count(); ++i) {
SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className), SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
className, i); className, i);
// FIXME: take current verb into consideration
fIntercepts[i].dump(pts, (SkPath::Verb) *verbs); fIntercepts[i].dump(pts, (SkPath::Verb) *verbs);
pts += *verbs++; pts += *verbs++;
} }
@ -822,10 +1022,12 @@ struct InEdge {
fWinding); fWinding);
SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className), SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
className, fContainsIntercepts); className, fContainsIntercepts);
SkDebugf("%*s.fIntersected=%d\n", tab + sizeof(className),
className, fIntersected);
} }
#endif #endif
// temporary data : move this to a separate struct? // FIXME: temporary data : move this to a separate struct?
SkTDArray<const InEdge*> fCached; // list of edges already intercepted SkTDArray<const InEdge*> fCached; // list of edges already intercepted
SkTArray<Intercepts> fIntercepts; // one per verb SkTArray<Intercepts> fIntercepts; // one per verb
@ -836,6 +1038,7 @@ struct InEdge {
int fID; int fID;
signed char fWinding; signed char fWinding;
bool fContainsIntercepts; bool fContainsIntercepts;
bool fIntersected;
}; };
class InEdgeBuilder { class InEdgeBuilder {
@ -849,10 +1052,19 @@ InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges
, fID(0) , fID(0)
, fHorizontalEdges(horizontalEdges) , fHorizontalEdges(horizontalEdges)
, fIgnoreHorizontal(ignoreHorizontal) , fIgnoreHorizontal(ignoreHorizontal)
, fContainsCurves(false)
{ {
walk(); walk();
} }
bool containsCurves() const {
return fContainsCurves;
}
int nextID() {
return ++fID;
}
protected: protected:
void addEdge() { void addEdge() {
@ -864,7 +1076,7 @@ void addEdge() {
bool complete() { bool complete() {
if (fCurrentEdge && fCurrentEdge->fVerbs.count()) { if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
fCurrentEdge->complete(fWinding, ++fID); fCurrentEdge->complete(fWinding, nextID());
fCurrentEdge = NULL; fCurrentEdge = NULL;
return true; return true;
} }
@ -913,9 +1125,11 @@ void walk() {
break; break;
case SkPath::kQuad_Verb: case SkPath::kQuad_Verb:
winding = direction(3); winding = direction(3);
fContainsCurves = true;
break; break;
case SkPath::kCubic_Verb: case SkPath::kCubic_Verb:
winding = direction(4); winding = direction(4);
fContainsCurves = true;
break; break;
case SkPath::kClose_Verb: case SkPath::kClose_Verb:
SkASSERT(fCurrentEdge); SkASSERT(fCurrentEdge);
@ -969,6 +1183,7 @@ private:
int fID; int fID;
int8_t fWinding; int8_t fWinding;
bool fIgnoreHorizontal; bool fIgnoreHorizontal;
bool fContainsCurves;
}; };
struct WorkEdge { struct WorkEdge {
@ -1134,8 +1349,8 @@ public:
} }
bool advanceT() { bool advanceT() {
SkASSERT(fTIndex <= fTs->count()); SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
return ++fTIndex <= fTs->count(); return ++fTIndex <= fTs->count() - fExplicitTs;
} }
bool advance() { bool advance() {
@ -1153,6 +1368,7 @@ public:
SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts); SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts);
} while (fWorkEdge.fPts[0].fY >= y); } while (fWorkEdge.fPts[0].fY >= y);
initT(); initT();
SkASSERT(!fExplicitTs);
fTIndex = fTs->count() + 1; fTIndex = fTs->count() + 1;
} }
@ -1187,7 +1403,7 @@ public:
// for the fTIndex, don't do it again // for the fTIndex, don't do it again
// For identical x, this lets us know which edge is first. // For identical x, this lets us know which edge is first.
// 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() - fExplicitTs) - 1;
double tAbove = t(fTIndex + add); double tAbove = t(fTIndex + add);
(*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove); (*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
double tBelow = t(fTIndex - ~add); double tBelow = t(fTIndex - ~add);
@ -1195,6 +1411,7 @@ public:
SkASSERT(tAbove != tBelow); SkASSERT(tAbove != tBelow);
// FIXME: this can loop forever // FIXME: this can loop forever
// need a break if we hit the end // need a break if we hit the end
// FIXME: in unit test, figure out how explicit Ts work as well
while (fAbove.fY == fBelow.fY) { while (fAbove.fY == fBelow.fY) {
if (add < 0 || fTIndex == fTs->count()) { if (add < 0 || fTIndex == fTs->count()) {
add -= 1; add -= 1;
@ -1249,7 +1466,8 @@ public:
const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front(); const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count()); SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex(); const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
fTs = &interceptPtr->fTs; fTs = &interceptPtr->fTs;
fExplicitTs = interceptPtr->fExplicit;
// the above is conceptually the same as // the above is conceptually the same as
// fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs; // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
// but templated arrays don't allow returning a pointer to the end() element // but templated arrays don't allow returning a pointer to the end() element
@ -1379,23 +1597,21 @@ public:
#endif #endif
return ulps >= 0 && ulps <= 32; return ulps >= 0 && ulps <= 32;
} }
double nextT() const { double nextT() const {
SkASSERT(fTIndex <= fTs->count()); SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
return t(fTIndex + 1); return t(fTIndex + 1);
} }
double t() const { double t() const {
if (fTIndex == 0) { return t(fTIndex);
return 0;
}
if (fTIndex > fTs->count()) {
return 1;
}
return (*fTs)[fTIndex - 1];
} }
double t(int tIndex) const { double t(int tIndex) const {
if (fExplicitTs) {
SkASSERT(tIndex < fTs->count());
return (*fTs)[tIndex];
}
if (tIndex == 0) { if (tIndex == 0) {
return 0; return 0;
} }
@ -1426,6 +1642,7 @@ public:
bool fCloseCall; bool fCloseCall;
bool fDone; bool fDone;
bool fFixBelow; bool fFixBelow;
bool fExplicitTs;
}; };
static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) { static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
@ -1462,24 +1679,39 @@ static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
HorizontalEdge** sorted = horizontal; HorizontalEdge** sorted = horizontal;
horzEdge = *sorted; horzEdge = *sorted;
do { do {
if (wt.verb() == SkPath::kLine_Verb) { double wtTs[4];
double wtTs[2]; int pts;
int pts = LineIntersect(wt.fPts, horzEdge->fLeft, uint8_t verb = wt.verb();
horzEdge->fRight, horzEdge->fY, wtTs); switch (verb) {
if (pts) { case SkPath::kLine_Verb:
pts = LineIntersect(wt.fPts, horzEdge->fLeft,
horzEdge->fRight, horzEdge->fY, wtTs);
break;
case SkPath::kQuad_Verb:
pts = QuadIntersect(wt.fPts, horzEdge->fLeft,
horzEdge->fRight, horzEdge->fY, wtTs);
break;
case SkPath::kCubic_Verb:
pts = CubicIntersect(wt.fPts, horzEdge->fLeft,
horzEdge->fRight, horzEdge->fY, wtTs);
break;
}
if (pts) {
#if DEBUG_ADD_BOTTOM_TS #if DEBUG_ADD_BOTTOM_TS
SkDebugf("%s y=%g wtTs[0]=%g (%g,%g, %g,%g)\n", __FUNCTION__, for (int x = 0; x < pts; ++x) {
horzEdge->fY, wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY, SkDebugf("%s y=%g wtTs[0]=%g (%g,%g", __FUNCTION__,
wt.fPts[1].fX, wt.fPts[1].fY); horzEdge->fY, wtTs[x], wt.fPts[0].fX, wt.fPts[0].fY);
if (pts == 2) { for (int y = 0; y < verb; ++y) {
SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]); SkDebugf(" %g,%g", wt.fPts[y + 1].fX, wt.fPts[y + 1].fY));
} }
#endif SkDebugf(")\n");
test->add(wtTs, pts, wt.verbIndex());
} }
} else { if (pts > verb) {
// FIXME: add all curve types SkASSERT(0); // FIXME ? should this work?
SkASSERT(0); SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
}
#endif
test->add(wtTs, pts, wt.verbIndex());
} }
horzEdge = *++sorted; horzEdge = *++sorted;
} while (horzEdge->fY == bottom } while (horzEdge->fY == bottom
@ -1526,6 +1758,9 @@ static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
InEdge** nextPtr = testPtr; InEdge** nextPtr = testPtr;
do { do {
InEdge* next = *++nextPtr; InEdge* next = *++nextPtr;
// FIXME: this compares against the sentinel sometimes
// OPTIMIZATION: this may never be needed since this gets called
// in two passes now. Verify that double hits are appropriate.
if (test->cached(next)) { if (test->cached(next)) {
continue; continue;
} }
@ -1695,6 +1930,9 @@ static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
} }
} while (wt.advance()); } while (wt.advance());
} }
#if DEBUG_BOTTOM
SkDebugf("%s bottom=%1.9g\n", __FUNCTION__, bottom);
#endif
return bottom; return bottom;
} }
@ -1729,6 +1967,9 @@ static SkScalar findBottom(InEdge** currentPtr,
} }
test = *++testPtr; test = *++testPtr;
} }
#if DEBUG_BOTTOM
SkDebugf("%s %d bottom=%1.9g\n", __FUNCTION__, activeEdges ? 2 : 1, bottom);
#endif
return bottom; return bottom;
} }
@ -2095,7 +2336,7 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
aboveBottom &= !activePtr->fCloseCall; aboveBottom &= !activePtr->fCloseCall;
} else { } else {
if (activePtr->fSkip || activePtr->fCloseCall) { if (activePtr->fSkip || activePtr->fCloseCall) {
SkDebugf("== %1.9g\n", clippedPts[0].fY); if (gShowDebugf) SkDebugf("== %1.9g\n", clippedPts[0].fY);
} }
} }
} while (moreToDo & aboveBottom); } while (moreToDo & aboveBottom);
@ -2103,6 +2344,16 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
} }
} }
static void dumpEdgeList(const SkTDArray<InEdge*>& edgeList,
const InEdge& edgeSentinel) {
#if DEBUG_DUMP
InEdge** debugPtr = edgeList.begin();
do {
(*debugPtr++)->dump();
} while (*debugPtr != &edgeSentinel);
#endif
}
void simplify(const SkPath& path, bool asFill, SkPath& simple) { void simplify(const SkPath& path, bool asFill, SkPath& simple) {
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
int windingMask = (path.getFillType() & 1) ? 1 : -1; int windingMask = (path.getFillType() & 1) ? 1 : -1;
@ -2149,14 +2400,24 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
y = bottom; y = bottom;
currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y); currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
} while (*currentPtr != &edgeSentinel); } while (*currentPtr != &edgeSentinel);
// if a quadratic or cubic now has an intermediate T value, see if the Ts
#if DEBUG_DUMP // on either side cause the Y values to monotonically increase. If not, split
InEdge** debugPtr = edgeList.begin(); // the curve at the new T.
do { if (builder.containsCurves()) {
(*debugPtr++)->dump(); currentPtr = edgeList.begin();
} while (*debugPtr != &edgeSentinel); SkTArray<InEdge> splits;
#endif do {
(*currentPtr)->splitInflectionPts(splits, builder.nextID());
} while (*++currentPtr != &edgeSentinel);
if (splits.count()) {
edges.pop_back(); // pop the sentinel
for (int index = 0; index < splits.count(); ++index) {
edges.push_back(splits[index]);
}
makeEdgeList(edges, edgeSentinel, edgeList);
}
}
dumpEdgeList(edgeList, edgeSentinel);
// walk the sorted edges from top to bottom, computing accumulated winding // walk the sorted edges from top to bottom, computing accumulated winding
SkTDArray<ActiveEdge> activeEdges; SkTDArray<ActiveEdge> activeEdges;
OutEdgeBuilder outBuilder(asFill); OutEdgeBuilder outBuilder(asFill);
@ -2166,21 +2427,12 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
SkScalar bottom = findBottom(currentPtr, edgeList.end(), SkScalar bottom = findBottom(currentPtr, edgeList.end(),
&activeEdges, y, asFill, lastPtr); &activeEdges, y, asFill, lastPtr);
#if DEBUG_BOTTOM
SkDebugf("%s findBottom bottom=%1.9g\n", __FUNCTION__, bottom);
#endif
if (lastPtr > currentPtr) { if (lastPtr > currentPtr) {
bottom = computeInterceptBottom(activeEdges, y, bottom); bottom = computeInterceptBottom(activeEdges, y, bottom);
#if DEBUG_BOTTOM
SkDebugf("%s computeInterceptBottom bottom=%1.9g\n", __FUNCTION__, bottom);
#endif
SkTDArray<ActiveEdge*> activeEdgeList; SkTDArray<ActiveEdge*> activeEdgeList;
sortHorizontal(activeEdges, activeEdgeList, y); sortHorizontal(activeEdges, activeEdgeList, y);
bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom, bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
outBuilder); outBuilder);
#if DEBUG_BOTTOM
SkDebugf("%s adjustCoincident bottom=%1.9g\n", __FUNCTION__, bottom);
#endif
stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder); stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder);
} }
y = bottom; y = bottom;

View File

@ -1,14 +1,24 @@
#include "EdgeWalker_Test.h" #include "EdgeWalker_Test.h"
#include "Intersection_Tests.h" #include "Intersection_Tests.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include <assert.h> #include <assert.h>
#include <pthread.h> #include <pthread.h>
struct State { struct State {
State() {
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100);
bitmap.allocPixels();
canvas = new SkCanvas(bitmap);
}
int a; int a;
int b; int b;
int c; int c;
int d; int d;
pthread_t threadID; pthread_t threadID;
SkCanvas* canvas;
SkBitmap bitmap;
bool abcIsATriangle; bool abcIsATriangle;
}; };
@ -77,13 +87,13 @@ static void* testSimplify4x4QuadralateralsMain(void* data)
str += sprintf(str, " path.lineTo(%d, %d);\n", hx, hy); str += sprintf(str, " path.lineTo(%d, %d);\n", hx, hy);
str += sprintf(str, " path.close();"); str += sprintf(str, " path.close();");
} }
if (!testSimplify(path, true, out)) { if (!testSimplify(path, true, out, state.bitmap, state.canvas)) {
SkDebugf("*/\n{ SkPath::kWinding_FillType, %d, %d, %d, %d," SkDebugf("*/\n{ SkPath::kWinding_FillType, %d, %d, %d, %d,"
" %d, %d, %d, %d },\n/*\n", state.a, state.b, state.c, state.d, " %d, %d, %d, %d },\n/*\n", state.a, state.b, state.c, state.d,
e, f, g, h); e, f, g, h);
} }
path.setFillType(SkPath::kEvenOdd_FillType); path.setFillType(SkPath::kEvenOdd_FillType);
if (!testSimplify(path, true, out)) { if (!testSimplify(path, true, out, state.bitmap, state.canvas)) {
SkDebugf("*/\n{ SkPath::kEvenOdd_FillType, %d, %d, %d, %d," SkDebugf("*/\n{ SkPath::kEvenOdd_FillType, %d, %d, %d, %d,"
" %d, %d, %d, %d },\n/*\n", state.a, state.b, state.c, state.d, " %d, %d, %d, %d },\n/*\n", state.a, state.b, state.c, state.d,
e, f, g, h); e, f, g, h);
@ -173,9 +183,9 @@ static void* testSimplify4x4NondegeneratesMain(void* data) {
str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy); str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy);
str += sprintf(str, " path.close();"); str += sprintf(str, " path.close();");
} }
testSimplify(path, true, out); testSimplify(path, true, out, state.bitmap, state.canvas);
path.setFillType(SkPath::kEvenOdd_FillType); path.setFillType(SkPath::kEvenOdd_FillType);
testSimplify(path, true, out); testSimplify(path, true, out, state.bitmap, state.canvas);
} }
} }
} }
@ -263,9 +273,9 @@ static void* testSimplify4x4DegeneratesMain(void* data) {
str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy); str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy);
str += sprintf(str, " path.close();"); str += sprintf(str, " path.close();");
} }
testSimplify(path, true, out); testSimplify(path, true, out, state.bitmap, state.canvas);
path.setFillType(SkPath::kEvenOdd_FillType); path.setFillType(SkPath::kEvenOdd_FillType);
testSimplify(path, true, out); testSimplify(path, true, out, state.bitmap, state.canvas);
} }
} }
} }

View File

@ -1,5 +1,6 @@
#include "EdgeWalker_Test.h" #include "EdgeWalker_Test.h"
#include "Intersection_Tests.h" #include "Intersection_Tests.h"
#include "SkBitmap.h"
// edges that didn't match // edges that didn't match
struct misMatch { struct misMatch {
@ -1579,6 +1580,7 @@ size_t misMatchCount = sizeof(misMatches) / sizeof(misMatches[0]);
void TestMismatches(); void TestMismatches();
void TestMismatches() { void TestMismatches() {
SkBitmap bitmap;
for (size_t index = 0; index < misMatchCount; ++index) { for (size_t index = 0; index < misMatchCount; ++index) {
const misMatch& miss = misMatches[index]; const misMatch& miss = misMatches[index];
int ax = miss.a & 0x03; int ax = miss.a & 0x03;
@ -1609,6 +1611,6 @@ void TestMismatches() {
path.lineTo(gx, gy); path.lineTo(gx, gy);
path.lineTo(hx, hy); path.lineTo(hx, hy);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
} }

View File

@ -1,5 +1,8 @@
#include "EdgeWalker_Test.h" #include "EdgeWalker_Test.h"
#include "Intersection_Tests.h" #include "Intersection_Tests.h"
#include "SkBitmap.h"
static SkBitmap bitmap;
static void testSimplifyTriangle() { static void testSimplifyTriangle() {
SkPath path, out; SkPath path, out;
@ -12,7 +15,7 @@ static void testSimplifyTriangle() {
path.lineTo(10,30); // /_| path.lineTo(10,30); // /_|
path.lineTo(20,30); path.lineTo(20,30);
path.close(); path.close();
testSimplify(path, true, out); // expect |\/| testSimplify(path, true, out, bitmap); // expect |\/|
// |__| // |__|
} }
@ -26,7 +29,7 @@ static void testSimplifyTriangle3() {
path.lineTo(1, 0); path.lineTo(1, 0);
path.lineTo(3, 1); path.lineTo(3, 1);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle4() { static void testSimplifyTriangle4() {
@ -39,7 +42,7 @@ static void testSimplifyTriangle4() {
path.lineTo(1, 0); path.lineTo(1, 0);
path.lineTo(2, 1); path.lineTo(2, 1);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle5() { static void testSimplifyTriangle5() {
@ -52,7 +55,7 @@ static void testSimplifyTriangle5() {
path.lineTo(1, 1); path.lineTo(1, 1);
path.lineTo(2, 1); path.lineTo(2, 1);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle6() { static void testSimplifyTriangle6() {
@ -67,7 +70,7 @@ static void testSimplifyTriangle6() {
path.lineTo(3, 1); path.lineTo(3, 1);
path.lineTo(0, 0); path.lineTo(0, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle7() { static void testSimplifyTriangle7() {
@ -82,7 +85,7 @@ static void testSimplifyTriangle7() {
path.lineTo(0, 2); path.lineTo(0, 2);
path.lineTo(0, 0); path.lineTo(0, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle8() { static void testSimplifyTriangle8() {
@ -97,7 +100,7 @@ static void testSimplifyTriangle8() {
path.lineTo(1, 3); path.lineTo(1, 3);
path.lineTo(0, 1); path.lineTo(0, 1);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle9() { static void testSimplifyTriangle9() {
@ -112,7 +115,7 @@ static void testSimplifyTriangle9() {
path.lineTo(2, 1); path.lineTo(2, 1);
path.lineTo(0, 0); path.lineTo(0, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle10() { static void testSimplifyTriangle10() {
@ -127,7 +130,7 @@ static void testSimplifyTriangle10() {
path.lineTo(0, 1); path.lineTo(0, 1);
path.lineTo(0, 0); path.lineTo(0, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle11() { static void testSimplifyTriangle11() {
@ -142,7 +145,7 @@ static void testSimplifyTriangle11() {
path.lineTo(2, 2); path.lineTo(2, 2);
path.lineTo(0, 0); path.lineTo(0, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle12() { static void testSimplifyTriangle12() {
@ -157,7 +160,7 @@ static void testSimplifyTriangle12() {
path.lineTo(1, 1); path.lineTo(1, 1);
path.lineTo(2, 0); path.lineTo(2, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle13() { static void testSimplifyTriangle13() {
@ -172,7 +175,7 @@ static void testSimplifyTriangle13() {
path.lineTo(1, 1); path.lineTo(1, 1);
path.lineTo(3, 0); path.lineTo(3, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle14() { static void testSimplifyTriangle14() {
@ -187,7 +190,7 @@ static void testSimplifyTriangle14() {
path.lineTo(0, 1); path.lineTo(0, 1);
path.lineTo(0, 0); path.lineTo(0, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle15() { static void testSimplifyTriangle15() {
@ -201,7 +204,7 @@ static void testSimplifyTriangle15() {
path.lineTo(0, 1); path.lineTo(0, 1);
path.lineTo(2, 2); path.lineTo(2, 2);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle16() { static void testSimplifyTriangle16() {
@ -214,7 +217,7 @@ static void testSimplifyTriangle16() {
path.lineTo(0, 1); path.lineTo(0, 1);
path.lineTo(1, 3); path.lineTo(1, 3);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle17() { static void testSimplifyTriangle17() {
@ -227,7 +230,7 @@ static void testSimplifyTriangle17() {
path.lineTo(1, 3); path.lineTo(1, 3);
path.lineTo(0, 1); path.lineTo(0, 1);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle18() { static void testSimplifyTriangle18() {
@ -240,7 +243,7 @@ static void testSimplifyTriangle18() {
path.lineTo(0, 1); path.lineTo(0, 1);
path.lineTo(0, 3); path.lineTo(0, 3);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle19() { static void testSimplifyTriangle19() {
@ -254,7 +257,7 @@ static void testSimplifyTriangle19() {
path.lineTo(1, 1); path.lineTo(1, 1);
path.lineTo(2, 1); path.lineTo(2, 1);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle20() { static void testSimplifyTriangle20() {
@ -267,7 +270,7 @@ static void testSimplifyTriangle20() {
path.lineTo(3, 2); path.lineTo(3, 2);
path.lineTo(0, 3); path.lineTo(0, 3);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle21() { static void testSimplifyTriangle21() {
@ -280,7 +283,7 @@ static void testSimplifyTriangle21() {
path.lineTo(2, 1); path.lineTo(2, 1);
path.lineTo(0, 3); path.lineTo(0, 3);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyDegenerateTriangle1() { static void testSimplifyDegenerateTriangle1() {
@ -293,7 +296,7 @@ static void testSimplifyDegenerateTriangle1() {
path.lineTo(0, 0); path.lineTo(0, 0);
path.lineTo(0, 0); path.lineTo(0, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyDegenerateTriangle2() { static void testSimplifyDegenerateTriangle2() {
@ -306,7 +309,7 @@ static void testSimplifyDegenerateTriangle2() {
path.lineTo(2, 2); path.lineTo(2, 2);
path.lineTo(3, 3); path.lineTo(3, 3);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyWindingParallelogram() { static void testSimplifyWindingParallelogram() {
@ -322,7 +325,7 @@ static void testSimplifyWindingParallelogram() {
path.lineTo(20,30); // /_/ path.lineTo(20,30); // /_/
path.lineTo(30,10); path.lineTo(30,10);
path.close(); path.close();
testSimplify(path, true, out); // expect _ testSimplify(path, true, out, bitmap); // expect _
// / \ . // / \ .
} // /___\ . } // /___\ .
@ -339,7 +342,7 @@ static void testSimplifyXorParallelogram() {
path.lineTo(20,30); // /_/ path.lineTo(20,30); // /_/
path.lineTo(30,10); path.lineTo(30,10);
path.close(); path.close();
testSimplify(path, true, out); // expect _ testSimplify(path, true, out, bitmap); // expect _
} // \ / } // \ /
static void testSimplifyTriangle2() { static void testSimplifyTriangle2() {
@ -353,9 +356,10 @@ static void testSimplifyTriangle2() {
path.lineTo(20,10); // \ | path.lineTo(20,10); // \ |
path.lineTo(20,30); // \| path.lineTo(20,30); // \|
path.close(); // _ path.close(); // _
testSimplify(path, true, out); // expect | | testSimplify(path, true, out, bitmap); // expect | |
} // |_| } // |_|
#if 0
static void testPathTriangleRendering() { static void testPathTriangleRendering() {
SkPath one, two; SkPath one, two;
one.moveTo(0, 0); one.moveTo(0, 0);
@ -375,10 +379,11 @@ static void testPathTriangleRendering() {
two.reset(); two.reset();
} }
} }
#endif
static void simplify(const char* functionName, const SkPath& path, static void simplify(const char* functionName, const SkPath& path,
bool fill, SkPath& out) { bool fill, SkPath& out) {
SkDebugf("%s\n", functionName); if (false) SkDebugf("%s\n", functionName);
simplify(path, fill, out); simplify(path, fill, out);
} }
@ -525,7 +530,7 @@ static void testSimplifyTriangle22() {
path.lineTo(0, 2); path.lineTo(0, 2);
path.lineTo(0, 1); path.lineTo(0, 1);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle23() { static void testSimplifyTriangle23() {
@ -538,7 +543,7 @@ static void testSimplifyTriangle23() {
path.lineTo(0, 1); path.lineTo(0, 1);
path.lineTo(1, 2); path.lineTo(1, 2);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyTriangle24() { static void testSimplifyTriangle24() {
@ -551,7 +556,7 @@ static void testSimplifyTriangle24() {
path.lineTo(1, 0); path.lineTo(1, 0);
path.lineTo(0, 1); path.lineTo(0, 1);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifySkinnyTriangle7() { static void testSimplifySkinnyTriangle7() {
@ -754,7 +759,7 @@ static void (*simplifyTests[])() = {
testSimplifyTriangle2, testSimplifyTriangle2,
testSimplifyWindingParallelogram, testSimplifyWindingParallelogram,
testSimplifyXorParallelogram, testSimplifyXorParallelogram,
testPathTriangleRendering, // testPathTriangleRendering,
}; };
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]); static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
@ -772,7 +777,7 @@ void SimplifyPolygonPaths_Test() {
for ( ; index < simplifyTestsCount; ++index) { for ( ; index < simplifyTestsCount; ++index) {
(*simplifyTests[index])(); (*simplifyTests[index])();
if (simplifyTests[index] == testSimplifySkinnyTriangle2) { if (simplifyTests[index] == testSimplifySkinnyTriangle2) {
SkDebugf("%s last fast skinny test\n", __FUNCTION__); if (false) SkDebugf("%s last fast skinny test\n", __FUNCTION__);
} }
firstTestComplete = true; firstTestComplete = true;
} }

View File

@ -1,5 +1,8 @@
#include "EdgeWalker_Test.h" #include "EdgeWalker_Test.h"
#include "Intersection_Tests.h" #include "Intersection_Tests.h"
#include "SkBitmap.h"
static SkBitmap bitmap;
static void testSimplifyQuad1() { static void testSimplifyQuad1() {
SkPath path, out; SkPath path, out;
@ -13,7 +16,7 @@ static void testSimplifyQuad1() {
path.lineTo(1, 3); path.lineTo(1, 3);
path.lineTo(1, 3); path.lineTo(1, 3);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyQuad2() { static void testSimplifyQuad2() {
@ -28,7 +31,7 @@ static void testSimplifyQuad2() {
path.lineTo(1, 1); path.lineTo(1, 1);
path.lineTo(0, 2); path.lineTo(0, 2);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyQuad3() { static void testSimplifyQuad3() {
@ -43,7 +46,7 @@ static void testSimplifyQuad3() {
path.lineTo(2, 1); path.lineTo(2, 1);
path.lineTo(0, 2); path.lineTo(0, 2);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyQuad4() { static void testSimplifyQuad4() {
@ -58,7 +61,7 @@ static void testSimplifyQuad4() {
path.lineTo(3, 1); path.lineTo(3, 1);
path.lineTo(3, 3); path.lineTo(3, 3);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyQuad5() { static void testSimplifyQuad5() {
@ -73,7 +76,7 @@ static void testSimplifyQuad5() {
path.lineTo(2, 1); path.lineTo(2, 1);
path.lineTo(0, 2); path.lineTo(0, 2);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyQuad6() { static void testSimplifyQuad6() {
@ -88,7 +91,7 @@ static void testSimplifyQuad6() {
path.lineTo(1, 1); path.lineTo(1, 1);
path.lineTo(2, 2); path.lineTo(2, 2);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void (*simplifyTests[])() = { static void (*simplifyTests[])() = {

View File

@ -1,5 +1,8 @@
#include "EdgeWalker_Test.h" #include "EdgeWalker_Test.h"
#include "Intersection_Tests.h" #include "Intersection_Tests.h"
#include "SkBitmap.h"
static SkBitmap bitmap;
static void testSimplifyQuadratic1() { static void testSimplifyQuadratic1() {
SkPath path, out; SkPath path, out;
@ -9,7 +12,7 @@ static void testSimplifyQuadratic1() {
path.moveTo(1, 0); path.moveTo(1, 0);
path.quadTo(0, 0, 0, 1); path.quadTo(0, 0, 0, 1);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyQuadratic2() { static void testSimplifyQuadratic2() {
@ -20,8 +23,7 @@ static void testSimplifyQuadratic2() {
path.moveTo(20, 0); path.moveTo(20, 0);
path.quadTo(0, 0, 0, 20); path.quadTo(0, 0, 0, 20);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
} }
static void testSimplifyQuadratic3() { static void testSimplifyQuadratic3() {
@ -32,11 +34,23 @@ static void testSimplifyQuadratic3() {
path.moveTo(0, 20); path.moveTo(0, 20);
path.quadTo(0, 0, 20, 0); path.quadTo(0, 0, 20, 0);
path.close(); path.close();
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
}
static void testSimplifyQuadratic4() {
SkPath path, out;
path.moveTo(0, 20);
path.quadTo(20, 0, 40, 20);
path.close();
path.moveTo(40, 10);
path.quadTo(20, 30, 0, 10);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true); drawAsciiPaths(path, out, true);
} }
static void (*simplifyTests[])() = { static void (*simplifyTests[])() = {
testSimplifyQuadratic4,
testSimplifyQuadratic3, testSimplifyQuadratic3,
testSimplifyQuadratic2, testSimplifyQuadratic2,
testSimplifyQuadratic1, testSimplifyQuadratic1,

View File

@ -1,5 +1,8 @@
#include "EdgeWalker_Test.h" #include "EdgeWalker_Test.h"
#include "Intersection_Tests.h" #include "Intersection_Tests.h"
#include "SkBitmap.h"
static SkBitmap bitmap;
static void testSimplifyCoincidentInner() { static void testSimplifyCoincidentInner() {
SkPath path, out; SkPath path, out;
@ -7,7 +10,7 @@ static void testSimplifyCoincidentInner() {
path.addRect(10, 10, 60, 60, SkPath::kCCW_Direction); path.addRect(10, 10, 60, 60, SkPath::kCCW_Direction);
path.addRect(20, 20, 50, 50, SkPath::kCW_Direction); path.addRect(20, 20, 50, 50, SkPath::kCW_Direction);
path.addRect(20, 30, 40, 40, SkPath::kCW_Direction); path.addRect(20, 30, 40, 40, SkPath::kCW_Direction);
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
static void testSimplifyCoincidentVertical() { static void testSimplifyCoincidentVertical() {
@ -304,7 +307,7 @@ static void testSimplifyOverlap() {
SkPath path, out; SkPath path, out;
path.addRect(rect1, static_cast<SkPath::Direction>(dir)); path.addRect(rect1, static_cast<SkPath::Direction>(dir));
path.addRect(rect2, static_cast<SkPath::Direction>(dir)); path.addRect(rect2, static_cast<SkPath::Direction>(dir));
testSimplify(path, true, out); testSimplify(path, true, out, bitmap);
} }
} }
} }

View File

@ -2,9 +2,13 @@
#include "ShapeOps.h" #include "ShapeOps.h"
extern bool comparePaths(const SkPath& one, const SkPath& two); class SkBitmap;
class SkCanvas;
//extern int comparePaths(const SkPath& one, const SkPath& two);
extern void comparePathsTiny(const SkPath& one, const SkPath& two); extern void comparePathsTiny(const SkPath& one, const SkPath& two);
extern bool drawAsciiPaths(const SkPath& one, const SkPath& two, extern bool drawAsciiPaths(const SkPath& one, const SkPath& two,
bool drawPaths); bool drawPaths);
extern void showPath(const SkPath& path, const char* str = NULL); extern void showPath(const SkPath& path, const char* str = NULL);
extern bool testSimplify(const SkPath& path, bool fill, SkPath& out); extern bool testSimplify(const SkPath& path, bool fill, SkPath& out,
SkBitmap& bitmap, SkCanvas* canvas = 0);

View File

@ -5,7 +5,7 @@
#include "SkPaint.h" #include "SkPaint.h"
static bool gShowPath = false; static bool gShowPath = false;
static bool gComparePaths = false; static bool gComparePaths = true;
static bool gDrawLastAsciiPaths = true; static bool gDrawLastAsciiPaths = true;
static bool gDrawAllAsciiPaths = false; static bool gDrawAllAsciiPaths = false;
static bool gShowAsciiPaths = false; static bool gShowAsciiPaths = false;
@ -43,17 +43,27 @@ void showPath(const SkPath& path, const char* str) {
} }
} }
static bool pathsDrawTheSame(const SkPath& one, const SkPath& two) { static int pathsDrawTheSame(const SkPath& one, const SkPath& two,
SkBitmap& bits, SkCanvas* c) {
SkCanvas* canvasPtr = c;
if (!c) {
canvasPtr = new SkCanvas(bits);
}
const SkRect& bounds1 = one.getBounds(); const SkRect& bounds1 = one.getBounds();
const SkRect& bounds2 = two.getBounds(); const SkRect& bounds2 = two.getBounds();
SkRect larger = bounds1; SkRect larger = bounds1;
larger.join(bounds2); larger.join(bounds2);
SkBitmap bits;
int bitWidth = SkScalarCeil(larger.width()) + 2; int bitWidth = SkScalarCeil(larger.width()) + 2;
int bitHeight = SkScalarCeil(larger.height()) + 2; int bitHeight = SkScalarCeil(larger.height()) + 2;
bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight); if (bits.width() < bitWidth * 2 || bits.height() < bitHeight) {
bits.allocPixels(); if (bits.width() >= 200) {
SkCanvas canvas(bits); SkDebugf("%s bitWidth=%d bitHeight=%d\n", __FUNCTION__, bitWidth, bitHeight);
}
bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
bits.allocPixels();
canvasPtr->setBitmapDevice(bits);
}
SkCanvas& canvas = *canvasPtr;
canvas.drawColor(SK_ColorWHITE); canvas.drawColor(SK_ColorWHITE);
SkPaint paint; SkPaint paint;
canvas.save(); canvas.save();
@ -64,17 +74,21 @@ static bool pathsDrawTheSame(const SkPath& one, const SkPath& two) {
canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1); canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
canvas.drawPath(two, paint); canvas.drawPath(two, paint);
canvas.restore(); canvas.restore();
int errors = 0;
for (int y = 0; y < bitHeight; ++y) { for (int y = 0; y < bitHeight; ++y) {
uint32_t* addr1 = bits.getAddr32(0, y); uint32_t* addr1 = bits.getAddr32(0, y);
uint32_t* addr2 = bits.getAddr32(bitWidth, y); uint32_t* addr2 = bits.getAddr32(bitWidth, y);
for (int x = 0; x < bitWidth; ++x) { for (int x = 0; x < bitWidth; ++x) {
if (addr1[x] != addr2[x]) { errors += addr1[x] != addr2[x];
return false;
break;
}
} }
} }
return true; if (!c) {
delete canvasPtr;
}
return errors;
}
void bitmapInit(SkBitmap& bits) {
} }
bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawAsciiPaths(const SkPath& one, const SkPath& two,
@ -130,8 +144,8 @@ bool drawAsciiPaths(const SkPath& one, const SkPath& two,
return true; return true;
} }
static bool scaledDrawTheSame(const SkPath& one, const SkPath& two, static int scaledDrawTheSame(const SkPath& one, const SkPath& two,
int a, int b, bool drawPaths) { int a, int b, bool drawPaths, SkBitmap& bitmap, SkCanvas* canvas) {
SkMatrix scale; SkMatrix scale;
scale.reset(); scale.reset();
float aScale = 1.21f; float aScale = 1.21f;
@ -140,8 +154,9 @@ static bool scaledDrawTheSame(const SkPath& one, const SkPath& two,
SkPath scaledOne, scaledTwo; SkPath scaledOne, scaledTwo;
one.transform(scale, &scaledOne); one.transform(scale, &scaledOne);
two.transform(scale, &scaledTwo); two.transform(scale, &scaledTwo);
if (pathsDrawTheSame(scaledOne, scaledTwo)) { int errors = pathsDrawTheSame(scaledOne, scaledTwo, bitmap, canvas);
return true; if (errors == 0) {
return 0;
} }
while (!drawAsciiPaths(scaledOne, scaledTwo, drawPaths)) { while (!drawAsciiPaths(scaledOne, scaledTwo, drawPaths)) {
scale.reset(); scale.reset();
@ -151,28 +166,36 @@ static bool scaledDrawTheSame(const SkPath& one, const SkPath& two,
one.transform(scale, &scaledOne); one.transform(scale, &scaledOne);
two.transform(scale, &scaledTwo); two.transform(scale, &scaledTwo);
} }
return false; return errors;
} }
bool comparePaths(const SkPath& one, const SkPath& two) { static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
if (pathsDrawTheSame(one, two)) { SkCanvas* canvas) {
return true; int errors = pathsDrawTheSame(one, two, bitmap, canvas);
if (errors == 0) {
return 0;
} }
drawAsciiPaths(one, two, gDrawAllAsciiPaths); drawAsciiPaths(one, two, gDrawAllAsciiPaths);
for (int x = 9; x <= 33; ++x) { for (int x = 9; x <= 33; ++x) {
if (scaledDrawTheSame(one, two, x, x - (x >> 2), gDrawAllAsciiPaths)) { errors = scaledDrawTheSame(one, two, x, x - (x >> 2), gDrawAllAsciiPaths,
return true; bitmap, canvas);
if (errors == 0) {
return 0;
} }
} }
if (!gDrawAllAsciiPaths) { if (!gDrawAllAsciiPaths) {
scaledDrawTheSame(one, two, 9, 7, gDrawLastAsciiPaths); errors = scaledDrawTheSame(one, two, 9, 7, false, bitmap, canvas);
if (errors > 4) {
scaledDrawTheSame(one, two, 9, 7, true, bitmap, canvas);
}
} }
if (gComparePathsAssert) { if (errors > 0) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors);
if (errors > 4 && gComparePathsAssert) {
showPath(one); showPath(one);
showPath(two, "simplified:"); showPath(two, "simplified:");
SkASSERT(0); SkASSERT(0);
} }
return false; return errors;
} }
// doesn't work yet // doesn't work yet
@ -206,7 +229,8 @@ void comparePathsTiny(const SkPath& one, const SkPath& two) {
} }
} }
bool testSimplify(const SkPath& path, bool fill, SkPath& out) { bool testSimplify(const SkPath& path, bool fill, SkPath& out, SkBitmap& bitmap,
SkCanvas* canvas) {
if (gShowPath) { if (gShowPath) {
showPath(path); showPath(path);
} }
@ -214,5 +238,5 @@ bool testSimplify(const SkPath& path, bool fill, SkPath& out) {
if (!gComparePaths) { if (!gComparePaths) {
return true; return true;
} }
return comparePaths(path, out); return comparePaths(path, out, bitmap, canvas) == 0;
} }

View File

@ -139,7 +139,25 @@ int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]) {
LineCubicIntersections c(cubic, *((_Line*) 0), tRange); LineCubicIntersections c(cubic, *((_Line*) 0), tRange);
return c.horizontalIntersect(y); return c.horizontalIntersect(y);
} }
int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
double tRange[3]) {
LineCubicIntersections c(cubic, *((_Line*) 0), tRange);
int result = c.horizontalIntersect(y);
for (int index = 0; index < result; ) {
double x, y;
xy_at_t(cubic, tRange[index], x, y);
if (x < left || x > right) {
if (--result > index) {
tRange[index] = tRange[result];
}
continue;
}
++index;
}
return result;
}
int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]) { int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]) {
LineCubicIntersections c(cubic, line, cRange); LineCubicIntersections c(cubic, line, cRange);
int roots; int roots;

View File

@ -131,6 +131,16 @@ bool intersect() {
return roots > 0; return roots > 0;
} }
int horizontalIntersect(double axisIntercept) {
double D = quad[2].y; // f
double E = quad[1].y; // e
double F = quad[0].y; // d
D += F - 2 * E; // D = d - 2*e + f
E -= F; // E = -(d - e)
F -= axisIntercept;
return quadraticRoots(D, E, F, intersections.fT[0]);
}
protected: protected:
double findLineT(double t) { double findLineT(double t) {
@ -156,7 +166,24 @@ Intersections& intersections;
bool moreHorizontal; bool moreHorizontal;
}; };
int horizontalIntersect(const Quadratic& quad, double left, double right,
double y, double tRange[2]) {
Intersections i;
LineQuadraticIntersections q(quad, *((_Line*) 0), i);
int result = q.horizontalIntersect(y);
int tCount = 0;
for (int index = 0; index < result; ++index) {
double x, y;
xy_at_t(quad, i.fT[0][index], x, y);
if (x < left || x > right) {
continue;
}
tRange[tCount++] = i.fT[0][index];
}
return tCount;
}
bool intersect(const Quadratic& quad, const _Line& line, Intersections& i) { bool intersect(const Quadratic& quad, const _Line& line, Intersections& i) {
LineQuadraticIntersections q(quad, line, i); LineQuadraticIntersections q(quad, line, i);
return q.intersect(); return q.intersect();

View File

@ -86,16 +86,22 @@ bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
double newMinT1 = interp(minT1, maxT1, minT); double newMinT1 = interp(minT1, maxT1, minT);
double newMaxT1 = interp(minT1, maxT1, maxT); double newMaxT1 = interp(minT1, maxT1, maxT);
split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1; split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1;
#define VERBOSE 0
#if VERBOSE
printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n", __FUNCTION__, depth, printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n", __FUNCTION__, depth,
splits, newMinT1, newMaxT1, minT1, maxT1, split); splits, newMinT1, newMaxT1, minT1, maxT1, split);
#endif
minT1 = newMinT1; minT1 = newMinT1;
maxT1 = newMaxT1; maxT1 = newMaxT1;
} else { } else {
double newMinT2 = interp(minT2, maxT2, minT); double newMinT2 = interp(minT2, maxT2, minT);
double newMaxT2 = interp(minT2, maxT2, maxT); double newMaxT2 = interp(minT2, maxT2, maxT);
split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit; split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit;
#define VERBOSE 0
#if VERBOSE
printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n", __FUNCTION__, depth, printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n", __FUNCTION__, depth,
splits, newMinT2, newMaxT2, minT2, maxT2, split); splits, newMinT2, newMaxT2, minT2, maxT2, split);
#endif
minT2 = newMinT2; minT2 = newMinT2;
maxT2 = newMaxT2; maxT2 = newMaxT2;
} }

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.googlecode.skia.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSMainNibFile</key>
<string>ShapeOpsDemo</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@ -1,110 +0,0 @@
#include "EdgeWalker_Test.h"
#include "ShapeOps.h"
#include "SkApplication.h"
#include "SkCanvas.h"
#include "SkEvent.h"
#include "SkGraphics.h"
#include "SkPaint.h"
SkCanvas* canvas = 0;
SkBitmap* bitmap;
static bool test15(SkCanvas* canvas) {
// Three circles bounce inside a rectangle. The circles describe three, four
// or five points which in turn describe a polygon. The polygon points
// bounce inside the circles. The circles rotate and scale over time. The
// polygons are combined into a single path, simplified, and stroked.
static int step = 0;
const int circles = 3;
int scales[circles];
int angles[circles];
int locs[circles * 2];
int pts[circles * 2 * 4];
int c, p;
for (c = 0; c < circles; ++c) {
scales[c] = abs(10 - (step + c * 4) % 21);
angles[c] = (step + c * 6) % 600;
locs[c * 2] = abs(130 - (step + c * 9) % 261);
locs[c * 2 + 1] = abs(170 - (step + c * 11) % 341);
for (p = 0; p < 4; ++p) {
pts[c * 8 + p * 2] = abs(90 - ((step + c * 121 + p * 13) % 190));
pts[c * 8 + p * 2 + 1] = abs(110 - ((step + c * 223 + p * 17) % 230));
}
}
SkPath path, out;
for (c = 0; c < circles; ++c) {
for (p = 0; p < 4; ++p) {
SkScalar x = pts[c * 8 + p * 2];
SkScalar y = pts[c * 8 + p * 2 + 1];
x *= 3 + scales[c] / 10.0f;
y *= 3 + scales[c] / 10.0f;
SkScalar angle = angles[c] * 3.1415f * 2 / 600;
SkScalar temp = x * cos(angle) - y * sin(angle);
y = x * sin(angle) + y * cos(angle);
x = temp;
x += locs[c * 2] * 200 / 130.0f;
y += locs[c * 2 + 1] * 200 / 170.0f;
x += 50;
// y += 200;
if (p == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.close();
}
showPath(path, "original:");
simplify(path, true, out);
showPath(out, "simplified:");
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(3);
paint.setColor(0x3F007fbF);
canvas->drawPath(path, paint);
paint.setColor(0xFF60FF00);
paint.setStrokeWidth(1);
canvas->drawPath(out, paint);
++step;
return true;
}
static bool (*tests[])(SkCanvas* ) = {
test15,
};
static size_t testsCount = sizeof(tests) / sizeof(tests[0]);
static bool (*firstTest)(SkCanvas*) = test15;
extern "C" void* getPixels(bool* animate);
extern "C" void unlockPixels();
extern "C" void* getPixels(bool* animate) {
if (!canvas) {
canvas = new SkCanvas();
bitmap = new SkBitmap();
SkBitmap::Config config = SkBitmap::kARGB_8888_Config;
bitmap->setConfig(config, 1100, 630);
bitmap->allocPixels();
bitmap->setIsOpaque(true);
canvas->setBitmapDevice(*bitmap);
}
canvas->drawColor(SK_ColorWHITE);
size_t index = 0;
if (index == 0 && firstTest) {
while (index < testsCount && tests[index] != firstTest) {
++index;
}
}
*animate = (tests[index])(canvas);
bitmap->lockPixels();
return bitmap->getPixels();
}
extern "C" void unlockPixels() {
bitmap->unlockPixels();
}

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@ -0,0 +1,20 @@
add unit test for quadratic horizontal intersection
add unit test for cubic horizontal intersection with left/right
add unit test for ActiveEdge::calcLeft (can currently loop forever)
does ActiveEdge::isCoincidentWith need to support quad, cubic?
figure out why variation in ActiveEdge::tooCloseToCall isn't better
why does 'lastPtr - 2' in addIntersectingTs break testSimplifyTriangle22?
add code to promote quad to cubic, or add quad/cubic intersection
figure out why testSimplifySkinnyTriangle13 fails
for quadratics and cubics, once various T values are added, see if consecutive
Ts have ys that go up instead of down. If so, the edge needs to be broken.
when splitting curves at inflection pts, should I retain the original curve
data and note that the first/last T are no longer 0/1 ?
I need to figure this out before I can proceed
would it make sense to leave the InEdge alone, and add multiple copies of
ActiveEdge, pointing to the same InEdge, where the copy has only the subset
of Ts that need to be walked in reverse order?

View File

@ -66,7 +66,6 @@
'../experimental/Intersection/QuadraticUtilities.cpp', '../experimental/Intersection/QuadraticUtilities.cpp',
'../experimental/Intersection/RectUtilities.cpp', '../experimental/Intersection/RectUtilities.cpp',
'../experimental/Intersection/TestUtilities.cpp', '../experimental/Intersection/TestUtilities.cpp',
'../experimental/Intersection/CubicIntersection.h',
'../experimental/Intersection/CubicIntersection_TestData.h', '../experimental/Intersection/CubicIntersection_TestData.h',
'../experimental/Intersection/CubicUtilities.h', '../experimental/Intersection/CubicUtilities.h',
'../experimental/Intersection/CurveIntersection.h', '../experimental/Intersection/CurveIntersection.h',
@ -87,6 +86,7 @@
'../experimental/Intersection/ShapeOps.h', '../experimental/Intersection/ShapeOps.h',
'../experimental/Intersection/TestUtilities.h', '../experimental/Intersection/TestUtilities.h',
'../experimental/Intersection/TSearch.h', '../experimental/Intersection/TSearch.h',
'../experimental/Intersection/thingsToDo.txt',
], ],
'dependencies': [ 'dependencies': [
'core.gyp:core', 'core.gyp:core',