зеркало из https://github.com/mozilla/moz-skia.git
shape ops work in progress
more quadratics work git-svn-id: http://skia.googlecode.com/svn/trunk@3643 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
Родитель
75589257c6
Коммит
fb173424e9
|
@ -16,7 +16,7 @@
|
|||
#include "ShapeOps.h"
|
||||
#include "TSearch.h"
|
||||
|
||||
#if 0 // set to 1 for no debugging whatsoever
|
||||
#if 01 // set to 1 for no debugging whatsoever
|
||||
const bool gShowDebugf = false; // FIXME: remove once debugging is complete
|
||||
|
||||
#define DEBUG_DUMP 0
|
||||
|
@ -25,13 +25,15 @@ const bool gShowDebugf = false; // FIXME: remove once debugging is complete
|
|||
#define DEBUG_ADD_BOTTOM_TS 0
|
||||
#define DEBUG_ABOVE_BELOW 0
|
||||
#define DEBUG_ACTIVE_LESS_THAN 0
|
||||
#define DEBUG_ASSEMBLE 0
|
||||
#define DEBUG_BRIDGE 0
|
||||
#define DEBUG_SORT_HORIZONTAL 0
|
||||
#define DEBUG_OUT 0
|
||||
#define DEBUG_OUT_LESS_THAN 0
|
||||
#define DEBUG_ADJUST_COINCIDENT 0
|
||||
#define DEBUG_BOTTOM 0
|
||||
#define DEBUG_SPLIT 0
|
||||
|
||||
#define DEBUG_STITCH_EDGE 0
|
||||
#else
|
||||
const bool gShowDebugf = true; // FIXME: remove once debugging is complete
|
||||
|
||||
|
@ -41,15 +43,25 @@ const bool gShowDebugf = true; // FIXME: remove once debugging is complete
|
|||
#define DEBUG_ADD_BOTTOM_TS 0
|
||||
#define DEBUG_ABOVE_BELOW 01
|
||||
#define DEBUG_ACTIVE_LESS_THAN 0
|
||||
#define DEBUG_ASSEMBLE 1
|
||||
#define DEBUG_BRIDGE 1
|
||||
#define DEBUG_SORT_HORIZONTAL 01
|
||||
#define DEBUG_OUT 01
|
||||
#define DEBUG_OUT_LESS_THAN 0
|
||||
#define DEBUG_ADJUST_COINCIDENT 1
|
||||
#define DEBUG_BOTTOM 0
|
||||
#define DEBUG_SPLIT 1
|
||||
#define DEBUG_STITCH_EDGE 1
|
||||
|
||||
#endif
|
||||
|
||||
#if DEBUG_ASSEMBLE || DEBUG_BRIDGE
|
||||
static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
|
||||
#endif
|
||||
#if DEBUG_STITCH_EDGE
|
||||
static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
|
||||
#endif
|
||||
|
||||
static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
|
||||
Intersections& intersections) {
|
||||
const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
|
||||
|
@ -196,7 +208,29 @@ static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
|
|||
sub[3].fX = SkDoubleToScalar(dst[3].x);
|
||||
sub[3].fY = SkDoubleToScalar(dst[3].y);
|
||||
}
|
||||
|
||||
|
||||
static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
|
||||
SkRect& bounds) {
|
||||
SkPoint dst[3];
|
||||
QuadSubDivide(a, startT, endT, dst);
|
||||
bounds.fLeft = bounds.fRight = dst[0].fX;
|
||||
bounds.fTop = bounds.fBottom = dst[0].fY;
|
||||
for (int index = 1; index < 3; ++index) {
|
||||
bounds.growToInclude(dst[index].fX, dst[index].fY);
|
||||
}
|
||||
}
|
||||
|
||||
static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
|
||||
SkRect& bounds) {
|
||||
SkPoint dst[4];
|
||||
CubicSubDivide(a, startT, endT, dst);
|
||||
bounds.fLeft = bounds.fRight = dst[0].fX;
|
||||
bounds.fTop = bounds.fBottom = dst[0].fY;
|
||||
for (int index = 1; index < 4; ++index) {
|
||||
bounds.growToInclude(dst[index].fX, dst[index].fY);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
list of edges
|
||||
bounds for edge
|
||||
|
@ -336,49 +370,57 @@ public:
|
|||
break;
|
||||
}
|
||||
int closeEdgeIndex = -listIndex - 1;
|
||||
// the curve is deferred and not added right away because the
|
||||
// following edge may extend the first curve.
|
||||
SkPoint firstPt, lastCurve[4];
|
||||
uint8_t lastVerb;
|
||||
#if DEBUG_ASSEMBLE
|
||||
int firstIndex, lastIndex;
|
||||
const int tab = 8;
|
||||
#endif
|
||||
bool doMove = true;
|
||||
int edgeIndex;
|
||||
do {
|
||||
SkPoint* ptArray = fEdges[listIndex].fPts;
|
||||
uint8_t verb = fEdges[listIndex].fVerb;
|
||||
SkPoint* start, * end;
|
||||
SkPoint* curve[4];
|
||||
if (advance < 0) {
|
||||
start = &ptArray[verb];
|
||||
end = &ptArray[0];
|
||||
curve[0] = &ptArray[verb];
|
||||
if (verb == SkPath::kCubic_Verb) {
|
||||
curve[1] = &ptArray[2];
|
||||
curve[2] = &ptArray[1];
|
||||
}
|
||||
curve[verb] = &ptArray[0];
|
||||
} else {
|
||||
start = &ptArray[0];
|
||||
end = &ptArray[verb];
|
||||
curve[0] = &ptArray[0];
|
||||
if (verb == SkPath::kCubic_Verb) {
|
||||
curve[1] = &ptArray[1];
|
||||
curve[2] = &ptArray[2];
|
||||
}
|
||||
curve[verb] = &ptArray[verb];
|
||||
}
|
||||
if (verb == SkPath::kQuad_Verb) {
|
||||
curve[1] = &ptArray[1];
|
||||
}
|
||||
if (doMove) {
|
||||
firstPt = *start;
|
||||
simple.moveTo(start->fX, start->fY);
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
|
||||
start->fX, start->fY);
|
||||
firstPt = *curve[0];
|
||||
simple.moveTo(curve[0]->fX, curve[0]->fY);
|
||||
#if DEBUG_ASSEMBLE
|
||||
SkDebugf("%s %d moveTo (%g,%g)\n", __FUNCTION__,
|
||||
listIndex + 1, curve[0]->fX, curve[0]->fY);
|
||||
firstIndex = listIndex;
|
||||
#endif
|
||||
for (int index = 0; index <= verb; ++index) {
|
||||
lastCurve[index] = *curve[index];
|
||||
}
|
||||
lastCurve[0] = *start;
|
||||
if (verb == SkPath::kQuad_Verb) {
|
||||
lastCurve[1] = ptArray[1];
|
||||
} else if (verb == SkPath::kCubic_Verb) {
|
||||
if (advance < 0) {
|
||||
lastCurve[1] = ptArray[2];
|
||||
lastCurve[2] = ptArray[1];
|
||||
} else {
|
||||
lastCurve[1] = ptArray[1];
|
||||
lastCurve[2] = ptArray[2];
|
||||
}
|
||||
}
|
||||
lastCurve[verb] = *end;
|
||||
lastVerb = verb;
|
||||
doMove = false;
|
||||
} else {
|
||||
bool gap = lastCurve[verb] != *start;
|
||||
if (gap) {
|
||||
bool gap = lastCurve[lastVerb] != *curve[0];
|
||||
if (gap || lastVerb != SkPath::kLine_Verb) { // output the accumulated curve before the gap
|
||||
// FIXME: see comment in bridge -- this probably
|
||||
// conceals errors
|
||||
SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10);
|
||||
SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY,
|
||||
curve[0]->fY) <= 10);
|
||||
switch (lastVerb) {
|
||||
case SkPath::kLine_Verb:
|
||||
simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
|
||||
|
@ -393,43 +435,41 @@ public:
|
|||
lastCurve[3].fX, lastCurve[3].fY);
|
||||
break;
|
||||
}
|
||||
if (gShowDebugf) {
|
||||
const char* verbStr[] = {"", "line", "quad", "cubic"};
|
||||
SkDebugf("%s %sTo-1 (%g,%g)\n", __FUNCTION__,
|
||||
verbStr[lastVerb], lastCurve[lastVerb].fX,
|
||||
lastCurve[lastVerb].fY);
|
||||
}
|
||||
#if DEBUG_ASSEMBLE
|
||||
SkDebugf("%*s %d %sTo (%g,%g)\n", tab, "", lastIndex + 1,
|
||||
kLVerbStr[lastVerb], lastCurve[lastVerb].fX,
|
||||
lastCurve[lastVerb].fY);
|
||||
#endif
|
||||
}
|
||||
if (gap || lastVerb != SkPath::kLine_Verb || !extendLine(lastCurve, *end)) {
|
||||
int firstCopy = 1;
|
||||
if (gap || (lastVerb == SkPath::kLine_Verb &&
|
||||
!extendLine(lastCurve, *curve[verb]))) {
|
||||
// FIXME: see comment in bridge -- this probably
|
||||
// conceals errors
|
||||
SkASSERT(lastCurve[lastVerb] == *start ||
|
||||
(fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10));
|
||||
simple.lineTo(start->fX, start->fY);
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
|
||||
start->fX, start->fY);
|
||||
}
|
||||
lastCurve[0] = *start;
|
||||
SkASSERT(lastCurve[lastVerb] == *curve[0] ||
|
||||
(fFill && UlpsDiff(lastCurve[lastVerb].fY,
|
||||
curve[0]->fY) <= 10));
|
||||
simple.lineTo(curve[0]->fX, curve[0]->fY);
|
||||
#if DEBUG_ASSEMBLE
|
||||
SkDebugf("%*s %d gap lineTo (%g,%g)\n", tab, "",
|
||||
lastIndex + 1, curve[0]->fX, curve[0]->fY);
|
||||
#endif
|
||||
firstCopy = 0;
|
||||
} else if (lastVerb != SkPath::kLine_Verb) {
|
||||
firstCopy = 0;
|
||||
}
|
||||
if (verb == SkPath::kQuad_Verb) {
|
||||
lastCurve[1] = ptArray[1];
|
||||
} else if (verb == SkPath::kCubic_Verb) {
|
||||
if (advance < 0) {
|
||||
lastCurve[1] = ptArray[2];
|
||||
lastCurve[2] = ptArray[1];
|
||||
} else {
|
||||
lastCurve[1] = ptArray[1];
|
||||
lastCurve[2] = ptArray[2];
|
||||
}
|
||||
for (int index = firstCopy; index <= verb; ++index) {
|
||||
lastCurve[index] = *curve[index];
|
||||
}
|
||||
lastCurve[verb] = *end;
|
||||
lastVerb = verb;
|
||||
}
|
||||
lastVerb = verb;
|
||||
#if DEBUG_ASSEMBLE
|
||||
lastIndex = listIndex;
|
||||
#endif
|
||||
if (advance < 0) {
|
||||
edgeIndex = fTops[listIndex];
|
||||
fTops[listIndex] = 0;
|
||||
} else {
|
||||
} else {
|
||||
edgeIndex = fBottoms[listIndex];
|
||||
fBottoms[listIndex] = 0;
|
||||
}
|
||||
|
@ -442,37 +482,44 @@ public:
|
|||
}
|
||||
}
|
||||
if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
|
||||
switch (lastVerb) {
|
||||
case SkPath::kLine_Verb:
|
||||
simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
|
||||
lastCurve[2].fX, lastCurve[2].fY);
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
|
||||
lastCurve[2].fX, lastCurve[2].fY,
|
||||
lastCurve[3].fX, lastCurve[3].fY);
|
||||
break;
|
||||
}
|
||||
#if DEBUG_ASSEMBLE
|
||||
SkDebugf("%*s %d %sTo last (%g, %g)\n", tab, "",
|
||||
lastIndex + 1, kLVerbStr[lastVerb],
|
||||
lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
|
||||
#endif
|
||||
if (lastCurve[lastVerb] != firstPt) {
|
||||
switch (lastVerb) {
|
||||
case SkPath::kLine_Verb:
|
||||
simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
|
||||
lastCurve[2].fX, lastCurve[2].fY);
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
|
||||
lastCurve[2].fX, lastCurve[2].fY,
|
||||
lastCurve[3].fX, lastCurve[3].fY);
|
||||
break;
|
||||
}
|
||||
if (gShowDebugf) {
|
||||
const char* verbStr[] = {"", "line", "quad", "cubic"};
|
||||
SkDebugf("%s %sTo last (%g, %g)\n", __FUNCTION__,
|
||||
verbStr[lastVerb],
|
||||
lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
|
||||
}
|
||||
simple.lineTo(firstPt.fX, firstPt.fY);
|
||||
#if DEBUG_ASSEMBLE
|
||||
SkDebugf("%*s %d final line (%g, %g)\n", tab, "",
|
||||
firstIndex + 1, firstPt.fX, firstPt.fY);
|
||||
#endif
|
||||
}
|
||||
simple.lineTo(firstPt.fX, firstPt.fY);
|
||||
simple.close();
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s close (%g, %g)\n", __FUNCTION__,
|
||||
firstPt.fX, firstPt.fY);
|
||||
}
|
||||
#if DEBUG_ASSEMBLE
|
||||
SkDebugf("%*s close\n", tab, "");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
// if this and next edge go different directions
|
||||
// if this and next edge go different directions
|
||||
#if DEBUG_ASSEMBLE
|
||||
SkDebugf("%*s advance=%d edgeIndex=%d flip=%s\n", tab, "",
|
||||
advance, edgeIndex, advance > 0 ^ edgeIndex < 0 ?
|
||||
"true" : "false");
|
||||
#endif
|
||||
if (advance > 0 ^ edgeIndex < 0) {
|
||||
advance = -advance;
|
||||
}
|
||||
|
@ -617,6 +664,22 @@ public:
|
|||
leftPt.fX, leftPt.fY);
|
||||
}
|
||||
SkASSERT(fMismatches.count() == 0);
|
||||
#endif
|
||||
#if DEBUG_BRIDGE
|
||||
for (index = 0; index < count; ++index) {
|
||||
const OutEdge& edge = fEdges[index];
|
||||
uint8_t verb = edge.fVerb;
|
||||
SkDebugf("%s %d edge=%d %s (%1.9g,%1.9g) (%1.9g,%1.9g)\n",
|
||||
index == 0 ? __FUNCTION__ : " ",
|
||||
index + 1, edge.fID, kLVerbStr[verb], edge.fPts[0].fX,
|
||||
edge.fPts[0].fY, edge.fPts[verb].fX, edge.fPts[verb].fY);
|
||||
}
|
||||
for (index = 0; index < count; ++index) {
|
||||
SkDebugf(" top of % 2d connects to %s of % 2d\n", index + 1,
|
||||
fTops[index] < 0 ? "top " : "bottom", abs(fTops[index]));
|
||||
SkDebugf(" bottom of % 2d connects to %s of % 2d\n", index + 1,
|
||||
fBottoms[index] < 0 ? "top " : "bottom", abs(fBottoms[index]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -696,6 +759,8 @@ public:
|
|||
className, fTopIntercepts);
|
||||
SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className),
|
||||
className, fBottomIntercepts);
|
||||
SkDebugf("%*s.fExplicit=%d\n", tab + sizeof(className),
|
||||
className, fExplicit);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -781,7 +846,7 @@ struct InEdge {
|
|||
return insertedAt;
|
||||
}
|
||||
|
||||
void addPartial(SkTArray<InEdge>& edges, int id, int ptStart, int ptEnd,
|
||||
void addPartial(SkTArray<InEdge>& edges, int ptStart, int ptEnd,
|
||||
int verbStart, int verbEnd) {
|
||||
InEdge* edge = edges.push_back_n(1);
|
||||
int verbCount = verbEnd - verbStart;
|
||||
|
@ -793,25 +858,35 @@ struct InEdge {
|
|||
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) {
|
||||
void addSplit(SkTArray<InEdge>& edges, SkPoint* pts, uint8_t verb,
|
||||
Intercepts& intercepts, int firstT, int lastT, bool flipped) {
|
||||
InEdge* edge = edges.push_back_n(1);
|
||||
edge->fIntercepts.push_back_n(1);
|
||||
edge->fIntercepts[0].fTs.append(tCount, ts);
|
||||
if (firstT == 0) {
|
||||
*edge->fIntercepts[0].fTs.append() = 0;
|
||||
} else {
|
||||
*edge->fIntercepts[0].fTs.append() = intercepts.fTs[firstT - 1];
|
||||
}
|
||||
bool add1 = lastT == intercepts.fTs.count();
|
||||
edge->fIntercepts[0].fTs.append(lastT - firstT, &intercepts.fTs[firstT]);
|
||||
if (add1) {
|
||||
*edge->fIntercepts[0].fTs.append() = 1;
|
||||
}
|
||||
edge->fIntercepts[0].fExplicit = true;
|
||||
edge->fPts.append(verb, pts);
|
||||
edge->fPts.append(verb + 1, pts);
|
||||
edge->fVerbs.append(1, &verb);
|
||||
edge->setBounds();
|
||||
edge->fID = id + edges.count();
|
||||
edge->fWinding = fWinding;
|
||||
// FIXME: bounds could be better for partial Ts
|
||||
edge->setSubBounds();
|
||||
edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
|
||||
if (flipped) {
|
||||
flip();
|
||||
edge->flipTs();
|
||||
edge->fWinding = -fWinding;
|
||||
} else {
|
||||
edge->fWinding = fWinding;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -831,17 +906,16 @@ struct InEdge {
|
|||
return false;
|
||||
}
|
||||
|
||||
void complete(signed char winding, int id) {
|
||||
void complete(signed char winding) {
|
||||
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() {
|
||||
void flip() {
|
||||
size_t index;
|
||||
size_t last = fPts.count() - 1;
|
||||
for (index = 0; index < last; ++index, --last) {
|
||||
|
@ -852,6 +926,18 @@ struct InEdge {
|
|||
SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
|
||||
}
|
||||
}
|
||||
|
||||
void flipTs() {
|
||||
SkASSERT(fIntercepts.count() == 1);
|
||||
Intercepts& intercepts = fIntercepts[0];
|
||||
SkASSERT(intercepts.fExplicit);
|
||||
SkTDArray<double>& ts = intercepts.fTs;
|
||||
size_t index;
|
||||
size_t last = ts.count() - 1;
|
||||
for (index = 0; index < last; ++index, --last) {
|
||||
SkTSwap<double>(ts[index], ts[last]);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
fCached.reset();
|
||||
|
@ -859,7 +945,6 @@ struct InEdge {
|
|||
fPts.reset();
|
||||
fVerbs.reset();
|
||||
fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
|
||||
fID = -1;
|
||||
fWinding = 0;
|
||||
fContainsIntercepts = false;
|
||||
fIntersected = false;
|
||||
|
@ -881,8 +966,25 @@ struct InEdge {
|
|||
++ptPtr;
|
||||
}
|
||||
}
|
||||
|
||||
// recompute bounds based on subrange of T values
|
||||
void setSubBounds() {
|
||||
SkASSERT(fIntercepts.count() == 1);
|
||||
Intercepts& intercepts = fIntercepts[0];
|
||||
SkASSERT(intercepts.fExplicit);
|
||||
SkASSERT(fVerbs.count() == 1);
|
||||
SkTDArray<double>& ts = intercepts.fTs;
|
||||
if (fVerbs[0] == SkPath::kQuad_Verb) {
|
||||
SkASSERT(fPts.count() == 3);
|
||||
QuadSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
|
||||
} else {
|
||||
SkASSERT(fVerbs[0] == SkPath::kCubic_Verb);
|
||||
SkASSERT(fPts.count() == 4);
|
||||
CubicSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
|
||||
}
|
||||
}
|
||||
|
||||
void splitInflectionPts(SkTArray<InEdge>& edges, int idStart) {
|
||||
void splitInflectionPts(SkTArray<InEdge>& edges) {
|
||||
if (!fIntersected) {
|
||||
return;
|
||||
}
|
||||
|
@ -918,11 +1020,9 @@ struct InEdge {
|
|||
int nextPt = pts - fPts.begin();
|
||||
int nextVerb = verbs - 1 - fVerbs.begin();
|
||||
if (lastVerb < nextVerb) {
|
||||
addPartial(edges, idStart, lastPt, nextPt,
|
||||
lastVerb, nextVerb);
|
||||
addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
|
||||
#if DEBUG_SPLIT
|
||||
SkDebugf("%s addPartial 1 edge=%d\n", __FUNCTION__,
|
||||
fID);
|
||||
SkDebugf("%s addPartial 1\n", __FUNCTION__);
|
||||
#endif
|
||||
}
|
||||
lastPt = nextPt;
|
||||
|
@ -931,20 +1031,18 @@ struct InEdge {
|
|||
} else {
|
||||
if (firstSplit >= 0) {
|
||||
if (lastSplit < firstSplit) {
|
||||
addSplit(edges, idStart, pts, verb,
|
||||
&intercepts.fTs[lastSplit],
|
||||
firstSplit - lastSplit, false);
|
||||
addSplit(edges, pts, verb, intercepts,
|
||||
lastSplit, firstSplit, false);
|
||||
#if DEBUG_SPLIT
|
||||
SkDebugf("%s addSplit 1 edge=%d tIndex=%d,%d\n",
|
||||
__FUNCTION__, fID, lastSplit, firstSplit);
|
||||
SkDebugf("%s addSplit 1 tIndex=%d,%d\n",
|
||||
__FUNCTION__, lastSplit, firstSplit);
|
||||
#endif
|
||||
}
|
||||
addSplit(edges, idStart, pts, verb,
|
||||
&intercepts.fTs[firstSplit],
|
||||
tIndex - firstSplit, true);
|
||||
addSplit(edges, pts, verb, intercepts,
|
||||
firstSplit, tIndex, true);
|
||||
#if DEBUG_SPLIT
|
||||
SkDebugf("%s addSplit 2 edge=%d tIndex=%d,%d flip\n",
|
||||
__FUNCTION__, fID, firstSplit, tIndex);
|
||||
SkDebugf("%s addSplit 2 tIndex=%d,%d flip\n",
|
||||
__FUNCTION__, firstSplit, tIndex);
|
||||
#endif
|
||||
lastSplit = tIndex;
|
||||
firstSplit = -1;
|
||||
|
@ -956,20 +1054,18 @@ struct InEdge {
|
|||
if (firstSplit < 0) {
|
||||
firstSplit = lastSplit;
|
||||
} else {
|
||||
addSplit(edges, idStart, pts, verb,
|
||||
&intercepts.fTs[lastSplit], firstSplit - lastSplit,
|
||||
false);
|
||||
addSplit(edges, pts, verb, intercepts, lastSplit,
|
||||
firstSplit, false);
|
||||
#if DEBUG_SPLIT
|
||||
SkDebugf("%s addSplit 3 edge=%d tIndex=%d,%d\n", __FUNCTION__,
|
||||
fID, lastSplit, firstSplit);
|
||||
SkDebugf("%s addSplit 3 tIndex=%d,%d\n", __FUNCTION__,
|
||||
lastSplit, firstSplit);
|
||||
#endif
|
||||
}
|
||||
addSplit(edges, idStart, pts, verb,
|
||||
&intercepts.fTs[firstSplit], tIndex - firstSplit,
|
||||
pts[verb].fY < y);
|
||||
addSplit(edges, pts, verb, intercepts, firstSplit,
|
||||
tIndex, 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" : "");
|
||||
SkDebugf("%s addSplit 4 tIndex=%d,%d %s\n", __FUNCTION__,
|
||||
firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -978,10 +1074,9 @@ struct InEdge {
|
|||
int nextVerb = verbs - 1 - fVerbs.begin();
|
||||
if (lastVerb < nextVerb) {
|
||||
int nextPt = pts - fPts.begin();
|
||||
addPartial(edges, idStart, lastPt, nextPt,
|
||||
lastVerb, nextVerb);
|
||||
addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
|
||||
#if DEBUG_SPLIT
|
||||
SkDebugf("%s addPartial 2 edge=%d\n", __FUNCTION__, fID);
|
||||
SkDebugf("%s addPartial 2\n", __FUNCTION__);
|
||||
#endif
|
||||
}
|
||||
// OPTIMIZATION: reuse the edge instead of marking it empty
|
||||
|
@ -1015,7 +1110,7 @@ struct InEdge {
|
|||
SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
|
||||
className, i, fVerbs[i]);
|
||||
}
|
||||
SkDebugf("%*s.fBounds=(%1.9g. %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
|
||||
SkDebugf("%*s.fBounds=(%1.9g, %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
|
||||
className, fBounds.fLeft, fBounds.fTop,
|
||||
fBounds.fRight, fBounds.fBottom);
|
||||
SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
|
||||
|
@ -1049,7 +1144,6 @@ InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges
|
|||
: fPath(path)
|
||||
, fCurrentEdge(NULL)
|
||||
, fEdges(edges)
|
||||
, fID(0)
|
||||
, fHorizontalEdges(horizontalEdges)
|
||||
, fIgnoreHorizontal(ignoreHorizontal)
|
||||
, fContainsCurves(false)
|
||||
|
@ -1061,10 +1155,6 @@ bool containsCurves() const {
|
|||
return fContainsCurves;
|
||||
}
|
||||
|
||||
int nextID() {
|
||||
return ++fID;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void addEdge() {
|
||||
|
@ -1076,7 +1166,7 @@ void addEdge() {
|
|||
|
||||
bool complete() {
|
||||
if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
|
||||
fCurrentEdge->complete(fWinding, nextID());
|
||||
fCurrentEdge->complete(fWinding);
|
||||
fCurrentEdge = NULL;
|
||||
return true;
|
||||
}
|
||||
|
@ -1180,7 +1270,6 @@ private:
|
|||
SkPath::Verb fVerb;
|
||||
int fPtCount;
|
||||
int fPtOffset;
|
||||
int fID;
|
||||
int8_t fWinding;
|
||||
bool fIgnoreHorizontal;
|
||||
bool fContainsCurves;
|
||||
|
@ -1729,19 +1818,17 @@ static void debugShowLineIntersection(int pts, const WorkEdge& wt,
|
|||
SkPoint wtOutPt, wnOutPt;
|
||||
LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
|
||||
LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
|
||||
SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
|
||||
SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
|
||||
__FUNCTION__,
|
||||
wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
|
||||
wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY,
|
||||
test->fID, next->fID);
|
||||
wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY);
|
||||
if (pts == 2) {
|
||||
SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
|
||||
}
|
||||
SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
|
||||
SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
|
||||
__FUNCTION__,
|
||||
wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
|
||||
wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY,
|
||||
test->fID, next->fID);
|
||||
wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY);
|
||||
if (pts == 2) {
|
||||
SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
|
||||
}
|
||||
|
@ -1979,10 +2066,15 @@ static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
|
|||
if (edgeCount == 0) {
|
||||
return;
|
||||
}
|
||||
int id = 0;
|
||||
for (size_t index = 0; index < edgeCount; ++index) {
|
||||
*edgeList.append() = &edges[index];
|
||||
InEdge& edge = edges[index];
|
||||
if (!edge.fWinding) {
|
||||
continue;
|
||||
}
|
||||
edge.fID = ++id;
|
||||
*edgeList.append() = &edge;
|
||||
}
|
||||
edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
|
||||
*edgeList.append() = &edgeSentinel;
|
||||
QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
|
||||
}
|
||||
|
@ -2240,7 +2332,6 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
|
|||
bool moreToDo, aboveBottom;
|
||||
do {
|
||||
double currentT = activePtr->t();
|
||||
SkASSERT(currentT < 1);
|
||||
const SkPoint* points = wt.fPts;
|
||||
double nextT;
|
||||
do {
|
||||
|
@ -2270,32 +2361,30 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
|
|||
}
|
||||
if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
|
||||
!= clipped[verb].fY : clipped[0] != clipped[verb])) {
|
||||
if (gShowDebugf) {
|
||||
const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
|
||||
SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
|
||||
" v=%d t=(%1.9g,%1.9g)\n", tab, "",
|
||||
verbStr[verb], clipped[0].fX, clipped[0].fY,
|
||||
clipped[verb].fX, clipped[verb].fY,
|
||||
activePtr->ID(),
|
||||
activePtr->fWorkEdge.fVerb
|
||||
- activePtr->fWorkEdge.fEdge->fVerbs.begin(),
|
||||
currentT, nextT);
|
||||
}
|
||||
#if DEBUG_STITCH_EDGE
|
||||
SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
|
||||
" v=%d t=(%1.9g,%1.9g)\n", tab, "",
|
||||
kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
|
||||
clipped[verb].fX, clipped[verb].fY,
|
||||
activePtr->ID(),
|
||||
activePtr->fWorkEdge.fVerb
|
||||
- activePtr->fWorkEdge.fEdge->fVerbs.begin(),
|
||||
currentT, nextT);
|
||||
#endif
|
||||
outBuilder.addCurve(clipped, (SkPath::Verb) verb,
|
||||
activePtr->fWorkEdge.fEdge->fID,
|
||||
activePtr->fCloseCall);
|
||||
} else {
|
||||
if (gShowDebugf ) {
|
||||
const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
|
||||
SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
|
||||
" edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
|
||||
verbStr[verb], clipped[0].fX, clipped[0].fY,
|
||||
clipped[verb].fX, clipped[verb].fY,
|
||||
activePtr->ID(),
|
||||
activePtr->fWorkEdge.fVerb
|
||||
- activePtr->fWorkEdge.fEdge->fVerbs.begin(),
|
||||
currentT, nextT);
|
||||
}
|
||||
#if DEBUG_STITCH_EDGE
|
||||
SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
|
||||
" edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
|
||||
kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
|
||||
clipped[verb].fX, clipped[verb].fY,
|
||||
activePtr->ID(),
|
||||
activePtr->fWorkEdge.fVerb
|
||||
- activePtr->fWorkEdge.fEdge->fVerbs.begin(),
|
||||
currentT, nextT);
|
||||
#endif
|
||||
}
|
||||
// by advancing fAbove/fBelow, the next call to sortHorizontal
|
||||
// will use these values if they're still valid instead of
|
||||
|
@ -2369,6 +2458,7 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
|
|||
InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
|
||||
SkTDArray<InEdge*> edgeList;
|
||||
InEdge edgeSentinel;
|
||||
edgeSentinel.reset();
|
||||
makeEdgeList(edges, edgeSentinel, edgeList);
|
||||
SkTDArray<HorizontalEdge*> horizontalList;
|
||||
HorizontalEdge horizontalSentinel;
|
||||
|
@ -2407,13 +2497,13 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
|
|||
currentPtr = edgeList.begin();
|
||||
SkTArray<InEdge> splits;
|
||||
do {
|
||||
(*currentPtr)->splitInflectionPts(splits, builder.nextID());
|
||||
(*currentPtr)->splitInflectionPts(splits);
|
||||
} 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]);
|
||||
}
|
||||
edgeList.reset();
|
||||
makeEdgeList(edges, edgeSentinel, edgeList);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,9 +58,13 @@ static void (*simplifyTests[])() = {
|
|||
|
||||
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
|
||||
|
||||
static void (*firstTest)() = 0;
|
||||
static void (*firstTest)() = testSimplifyQuadratic3;
|
||||
static bool skipAll = false;
|
||||
|
||||
void SimplifyQuadraticPaths_Test() {
|
||||
if (skipAll) {
|
||||
return;
|
||||
}
|
||||
size_t index = 0;
|
||||
if (firstTest) {
|
||||
while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "SkBitmap.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkPaint.h"
|
||||
#include <algorithm>
|
||||
|
||||
static bool gShowPath = false;
|
||||
static bool gComparePaths = true;
|
||||
|
@ -124,7 +125,7 @@ bool drawAsciiPaths(const SkPath& one, const SkPath& two,
|
|||
canvas.drawPath(one, paint);
|
||||
canvas.restore();
|
||||
canvas.save();
|
||||
canvas.translate(-bounds2.fLeft + 1 + bitWidth, -bounds2.fTop + 1);
|
||||
canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
|
||||
canvas.drawPath(two, paint);
|
||||
canvas.restore();
|
||||
for (int y = 0; y < bitHeight; ++y) {
|
||||
|
@ -184,13 +185,19 @@ static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
|
|||
}
|
||||
}
|
||||
if (!gDrawAllAsciiPaths) {
|
||||
errors = scaledDrawTheSame(one, two, 9, 7, false, bitmap, canvas);
|
||||
if (errors > 4) {
|
||||
scaledDrawTheSame(one, two, 9, 7, true, bitmap, canvas);
|
||||
const SkRect& bounds1 = one.getBounds();
|
||||
const SkRect& bounds2 = two.getBounds();
|
||||
SkRect larger = bounds1;
|
||||
larger.join(bounds2);
|
||||
SkScalar xScale = std::max(80.0f / larger.width(), 1.0f);
|
||||
SkScalar yScale = std::max(60.0f / larger.height(), 1.0f);
|
||||
errors = scaledDrawTheSame(one, two, xScale, yScale, false, bitmap, canvas);
|
||||
if (errors > 8) {
|
||||
scaledDrawTheSame(one, two, xScale, yScale, true, bitmap, canvas);
|
||||
}
|
||||
}
|
||||
if (errors > 0) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors);
|
||||
if (errors > 4 && gComparePathsAssert) {
|
||||
if (errors > 31 && gComparePathsAssert) {
|
||||
showPath(one);
|
||||
showPath(two, "simplified:");
|
||||
SkASSERT(0);
|
||||
|
|
|
@ -94,13 +94,6 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
FE74136014F6866000056D7B /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = FEF87C2413E0410900335C58 /* opts.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = ECDC7853EF9A45553165AE98;
|
||||
remoteInfo = opts_ssse3;
|
||||
};
|
||||
FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = FEA5F4D91497FFF6005052F9 /* ports.xcodeproj */;
|
||||
|
@ -108,6 +101,13 @@
|
|||
remoteGlobalIDString = CDE03B47AA5CD6CE32E53995;
|
||||
remoteInfo = ports;
|
||||
};
|
||||
FEE70DA6153487F200814606 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = FEF87C2413E0410900335C58 /* opts.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = ECDC7853EF9A45553165AE98 /* libopts_ssse3.a */;
|
||||
remoteInfo = opts_ssse3;
|
||||
};
|
||||
FEED7261144DD38D0059E97B /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = FEED725D144DD38D0059E97B /* views.xcodeproj */;
|
||||
|
@ -670,7 +670,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
FEF87C2C13E0410900335C58 /* libopts.a */,
|
||||
FE74136114F6866000056D7B /* libopts_ssse3.a */,
|
||||
FEE70DA7153487F200814606 /* libopts_ssse3.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -791,13 +791,6 @@
|
|||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXReferenceProxy section */
|
||||
FE74136114F6866000056D7B /* libopts_ssse3.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libopts_ssse3.a;
|
||||
remoteRef = FE74136014F6866000056D7B /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
FEA5F4E11497FFF6005052F9 /* libports.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
|
@ -805,6 +798,13 @@
|
|||
remoteRef = FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
FEE70DA7153487F200814606 /* libopts_ssse3.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libopts_ssse3.a;
|
||||
remoteRef = FEE70DA6153487F200814606 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
FEED7262144DD38D0059E97B /* libviews.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
|
|
|
@ -69,11 +69,55 @@ path.lineTo(460.782654, -112.96492);
|
|||
path.lineTo(326.610535, 34.0393639);
|
||||
path.close();
|
||||
</div>
|
||||
<div id="test_5div">
|
||||
original:
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(20, 0, 20, 20);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(0, 20);
|
||||
path.quadTo(0, 0, 20, 0);
|
||||
path.lineTo(0, 20);
|
||||
path.close();
|
||||
simplified:
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(5, 5);
|
||||
path.quadTo(0, 10, 0, 20);
|
||||
path.lineTo(20, 20);
|
||||
path.quadTo(20, 10, 15, 5);
|
||||
path.lineTo(20, 0);
|
||||
path.quadTo(14.1421356, 0, 10, 1.71572876);
|
||||
path.quadTo(5.85786438, 0, 0, 0);
|
||||
path.close();
|
||||
</div>
|
||||
<div id="test_6div">
|
||||
original:
|
||||
path.moveTo(0, 20);
|
||||
path.quadTo(20, 0, 40, 20);
|
||||
path.lineTo(0, 20);
|
||||
path.close();
|
||||
path.moveTo(40, 10);
|
||||
path.quadTo(20, 30, 0, 10);
|
||||
path.lineTo(40, 10);
|
||||
path.close();
|
||||
simplified:
|
||||
path.moveTo(0, 10);
|
||||
path.quadTo(2.92893219, 12.9289322, 5.85786438, 15);
|
||||
path.quadTo(2.92893219, 17.0710678, 0, 20);
|
||||
path.lineTo(40, 20);
|
||||
path.quadTo(37.0710678, 17.0710678, 34.1421356, 15);
|
||||
path.quadTo(37.0710678, 12.9289322, 40, 10);
|
||||
path.lineTo(0, 10);
|
||||
path.close();
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var testDivs = [
|
||||
test_5div,
|
||||
test_6div,
|
||||
test_4div,
|
||||
test_3div,
|
||||
test_2div,
|
||||
|
@ -96,16 +140,26 @@ function parse(test) {
|
|||
var contourStrs = test.split("path.close();");
|
||||
var pattern = /-?\d+\.*\d*/g;
|
||||
for (var c in contourStrs) {
|
||||
var points = contourStrs[c].match(pattern);
|
||||
var pts = [];
|
||||
for (var wd in points) {
|
||||
var num = parseFloat(points[wd]);
|
||||
if (isNaN(num)) continue;
|
||||
pts.push(num );
|
||||
var contour = contourStrs[c];
|
||||
var verbStrs = contour.split("path");
|
||||
var verbs = [];
|
||||
for (var v in verbStrs) {
|
||||
var verbStr = verbStrs[v];
|
||||
var points = verbStr.match(pattern);
|
||||
var pts = [];
|
||||
for (var wd in points) {
|
||||
var num = parseFloat(points[wd]);
|
||||
if (isNaN(num)) continue;
|
||||
pts.push(num);
|
||||
}
|
||||
if (pts.length > 0)
|
||||
verbs.push(pts);
|
||||
}
|
||||
contours.push(pts);
|
||||
if (verbs.length > 0)
|
||||
contours.push(verbs);
|
||||
}
|
||||
tests.push(contours);
|
||||
if (contours.length > 0)
|
||||
tests.push(contours);
|
||||
}
|
||||
|
||||
function init(test) {
|
||||
|
@ -116,13 +170,15 @@ function init(test) {
|
|||
var xmax = -Infinity;
|
||||
var ymin = Infinity;
|
||||
var ymax = -Infinity;
|
||||
for (pts in test) {
|
||||
var pt = test[pts];
|
||||
for (i = 0; i < pt.length; i += 2) {
|
||||
xmin = Math.min(xmin, pt[i]);
|
||||
ymin = Math.min(ymin, pt[i + 1]);
|
||||
xmax = Math.max(xmax, pt[i]);
|
||||
ymax = Math.max(ymax, pt[i + 1]);
|
||||
for (var contours in test) {
|
||||
var contour = test[contours];
|
||||
for (var verbs in contour) {
|
||||
var verb = contour[verbs];
|
||||
var last = verb.length;
|
||||
xmin = Math.min(xmin, verb[0]);
|
||||
ymin = Math.min(ymin, verb[1]);
|
||||
xmax = Math.max(xmax, verb[last - 2]);
|
||||
ymax = Math.max(ymax, verb[last - 1]);
|
||||
}
|
||||
}
|
||||
var subscale = 1;
|
||||
|
@ -183,27 +239,46 @@ function draw(test, _at_x, _at_y, scale) {
|
|||
ctx.fillText(num.toFixed(0), 0, i * unit + _at_y + 0);
|
||||
}
|
||||
ctx.strokeStyle = "red";
|
||||
for (pts in test) {
|
||||
var pt = test[pts];
|
||||
var x = pt[0];
|
||||
var y = pt[1];
|
||||
var contours, verbs, pts;
|
||||
for (contours in test) {
|
||||
var contour = test[contours];
|
||||
if (contours == 2) ctx.strokeStyle = "blue";
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(xoffset + x * unit, yoffset + y * unit);
|
||||
for (i = 2; i < pt.length; i += 2) {
|
||||
x = pt[i];
|
||||
y = pt[i + 1];
|
||||
ctx.lineTo(xoffset + x * unit, yoffset + y * unit);
|
||||
var first = true;
|
||||
for (verbs in contour) {
|
||||
var verb = contour[verbs];
|
||||
switch (verb.length) {
|
||||
case 2:
|
||||
if (first) {
|
||||
ctx.moveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit);
|
||||
first = false;
|
||||
} else
|
||||
ctx.lineTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit);
|
||||
break;
|
||||
case 4:
|
||||
ctx.quadraticCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit,
|
||||
xoffset + verb[2] * unit, yoffset + verb[3] * unit);
|
||||
break;
|
||||
case 6:
|
||||
ctx.bezierCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit,
|
||||
xoffset + verb[2] * unit, yoffset + verb[3] * unit,
|
||||
xoffset + verb[4] * unit, yoffset + verb[5] * unit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.fillStyle="blue";
|
||||
for (pts in test) {
|
||||
var pt = test[pts];
|
||||
for (i = 0; i < pt.length; i += 2) {
|
||||
x = pt[i];
|
||||
y = pt[i + 1];
|
||||
drawPoint(x, y, xoffset, yoffset, unit);
|
||||
for (contours in test) {
|
||||
var contour = test[contours];
|
||||
for (verbs in contour) {
|
||||
var verb = contour[verbs];
|
||||
for (i = 0; i < verb.length; i += 2) {
|
||||
x = verb[i];
|
||||
y = verb[i + 1];
|
||||
drawPoint(x, y, xoffset, yoffset, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче