work in progress
in the middle of switching to sortless version git-svn-id: http://skia.googlecode.com/svn/trunk@3768 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
8e124a2454
commit
fa0588ff67
@ -74,9 +74,9 @@ void ActiveEdge_Test() {
|
||||
right.fWorkEdge.fEdge = &rightIn;
|
||||
for (size_t x = 0; x < leftRightCount; ++x) {
|
||||
left.fAbove = leftRight[x][0];
|
||||
left.fBelow = leftRight[x][1];
|
||||
left.fTangent = left.fBelow = leftRight[x][1];
|
||||
right.fAbove = leftRight[x][2];
|
||||
right.fBelow = leftRight[x][3];
|
||||
right.fTangent = right.fBelow = leftRight[x][3];
|
||||
SkASSERT(left < right);
|
||||
SkASSERT(operator_less_than(left, right));
|
||||
SkASSERT(!(right < left));
|
||||
|
@ -1,10 +1,63 @@
|
||||
/*
|
||||
* CubicBounds.cpp
|
||||
* edge
|
||||
*
|
||||
* Created by Cary Clark on 1/27/12.
|
||||
* Copyright 2012 __MyCompanyName__. All rights reserved.
|
||||
*
|
||||
*/
|
||||
#include "DataTypes.h"
|
||||
#include "Extrema.h"
|
||||
|
||||
static int isBoundedByEndPoints(double a, double b, double c, double d)
|
||||
{
|
||||
return (a <= b && a <= c && b <= d && c <= d)
|
||||
|| (a >= b && a >= c && b >= d && c >= d);
|
||||
}
|
||||
|
||||
double leftMostT(const Cubic& cubic, double startT, double endT) {
|
||||
double leftTs[2];
|
||||
_Point pt[2];
|
||||
int results = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x,
|
||||
leftTs);
|
||||
int best = -1;
|
||||
for (int index = 0; index < results; ++index) {
|
||||
if (startT > leftTs[index] || leftTs[index] > endT) {
|
||||
continue;
|
||||
}
|
||||
if (best < 0) {
|
||||
best = index;
|
||||
continue;
|
||||
}
|
||||
xy_at_t(cubic, leftTs[0], pt[0].x, pt[0].y);
|
||||
xy_at_t(cubic, leftTs[1], pt[1].x, pt[1].y);
|
||||
if (pt[0].x > pt[1].x) {
|
||||
best = 1;
|
||||
}
|
||||
}
|
||||
if (best >= 0) {
|
||||
return leftTs[best];
|
||||
}
|
||||
xy_at_t(cubic, startT, pt[0].x, pt[0].y);
|
||||
xy_at_t(cubic, endT, pt[1].x, pt[1].y);
|
||||
return pt[0].x <= pt[1].x ? startT : endT;
|
||||
}
|
||||
|
||||
void _Rect::setBounds(const Cubic& cubic) {
|
||||
set(cubic[0]);
|
||||
add(cubic[3]);
|
||||
double tValues[4];
|
||||
int roots = 0;
|
||||
if (!isBoundedByEndPoints(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x)) {
|
||||
roots = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x,
|
||||
cubic[3].x, tValues);
|
||||
}
|
||||
if (!isBoundedByEndPoints(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y)) {
|
||||
roots += findExtrema(cubic[0].y, cubic[1].y, cubic[2].y,
|
||||
cubic[3].y, &tValues[roots]);
|
||||
}
|
||||
for (int x = 0; x < roots; ++x) {
|
||||
_Point result;
|
||||
xy_at_t(cubic, tValues[x], result.x, result.y);
|
||||
add(result);
|
||||
}
|
||||
}
|
||||
|
||||
void _Rect::setRawBounds(const Cubic& cubic) {
|
||||
set(cubic[0]);
|
||||
for (int x = 1; x < 4; ++x) {
|
||||
add(cubic[x]);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ static int vertical_line(const Cubic& cubic, Cubic& reduction) {
|
||||
reduction[1] = cubic[3];
|
||||
int smaller = reduction[1].y > reduction[0].y;
|
||||
int larger = smaller ^ 1;
|
||||
int roots = SkFindCubicExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
|
||||
int roots = findExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
|
||||
for (int index = 0; index < roots; ++index) {
|
||||
double yExtrema = interp_cubic_coords(&cubic[0].y, tValues[index]);
|
||||
if (reduction[smaller].y > yExtrema) {
|
||||
@ -44,7 +44,7 @@ static int horizontal_line(const Cubic& cubic, Cubic& reduction) {
|
||||
reduction[1] = cubic[3];
|
||||
int smaller = reduction[1].x > reduction[0].x;
|
||||
int larger = smaller ^ 1;
|
||||
int roots = SkFindCubicExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
|
||||
int roots = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
|
||||
for (int index = 0; index < roots; ++index) {
|
||||
double xExtrema = interp_cubic_coords(&cubic[0].x, tValues[index]);
|
||||
if (reduction[smaller].x > xExtrema) {
|
||||
@ -127,9 +127,9 @@ static int check_linear(const Cubic& cubic, Cubic& reduction,
|
||||
double tValues[2];
|
||||
int roots;
|
||||
if (useX) {
|
||||
roots = SkFindCubicExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
|
||||
roots = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
|
||||
} else {
|
||||
roots = SkFindCubicExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
|
||||
roots = findExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
|
||||
}
|
||||
for (index = 0; index < roots; ++index) {
|
||||
_Point extrema;
|
||||
|
@ -6,6 +6,7 @@
|
||||
class Intersections;
|
||||
|
||||
// unit-testable utilities
|
||||
double axialIntersect(const Quadratic& q1, const _Point& p, bool vert);
|
||||
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);
|
||||
@ -34,11 +35,26 @@ int reduceOrder(const Quadratic& quad, Quadratic& reduction);
|
||||
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 Cubic& cubic, double left, double right, double y,
|
||||
bool flipped, Intersections&);
|
||||
int horizontalIntersect(const _Line& line, double left, double right,
|
||||
double y, bool flipped, Intersections& );
|
||||
int horizontalIntersect(const Quadratic& quad, double left, double right,
|
||||
double y, double tRange[2]);
|
||||
int horizontalIntersect(const Quadratic& quad, double left, double right,
|
||||
double y, bool flipped, Intersections& );
|
||||
bool intersect(const Cubic& cubic1, const Cubic& cubic2, Intersections& );
|
||||
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& quad, const _Line& line, Intersections& );
|
||||
double leftMostT(const Cubic& , double startT, double endT);
|
||||
double leftMostT(const _Line& , double startT, double endT);
|
||||
double leftMostT(const Quadratic& , double startT, double endT);
|
||||
int verticalIntersect(const Cubic& cubic, double top, double bottom, double x,
|
||||
bool flipped, Intersections& );
|
||||
int verticalIntersect(const _Line& line, double top, double bottom, double x,
|
||||
bool flipped, Intersections& );
|
||||
int verticalIntersect(const Quadratic& quad, double top, double bottom,
|
||||
double x, bool flipped, Intersections& );
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
@ -20,9 +19,9 @@
|
||||
#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
|
||||
|
||||
// FIXME: remove once debugging is complete
|
||||
#if 0 // set to 1 for no debugging whatsoever
|
||||
#if 01 // set to 1 for no debugging whatsoever
|
||||
|
||||
const bool gRunTestsInOneThread = true;
|
||||
const bool gRunTestsInOneThread = false;
|
||||
|
||||
#define DEBUG_ACTIVE_LESS_THAN 0
|
||||
#define DEBUG_ADD 0
|
||||
@ -1381,23 +1380,51 @@ struct WorkEdge {
|
||||
class ActiveEdge {
|
||||
public:
|
||||
// this logic must be kept in sync with tooCloseToCall
|
||||
// callers expect this to only read fAbove, fBelow
|
||||
// callers expect this to only read fAbove, fTangent
|
||||
bool operator<(const ActiveEdge& rh) const {
|
||||
double topD = fAbove.fX - rh.fAbove.fX;
|
||||
if (rh.fAbove.fY < fAbove.fY) {
|
||||
topD = (rh.fBelow.fY - rh.fAbove.fY) * topD
|
||||
- (fAbove.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
|
||||
} else if (rh.fAbove.fY > fAbove.fY) {
|
||||
topD = (fBelow.fY - fAbove.fY) * topD
|
||||
+ (rh.fAbove.fY - fAbove.fY) * (fBelow.fX - fAbove.fX);
|
||||
if (fVerb == rh.fVerb) {
|
||||
// FIXME: don't know what to do if verb is quad, cubic
|
||||
return abCompare(fAbove, fBelow, rh.fAbove, rh.fBelow);
|
||||
}
|
||||
double botD = fBelow.fX - rh.fBelow.fX;
|
||||
if (rh.fBelow.fY > fBelow.fY) {
|
||||
botD = (rh.fBelow.fY - rh.fAbove.fY) * botD
|
||||
- (fBelow.fY - rh.fBelow.fY) * (rh.fBelow.fX - rh.fAbove.fX);
|
||||
} else if (rh.fBelow.fY < fBelow.fY) {
|
||||
botD = (fBelow.fY - fAbove.fY) * botD
|
||||
+ (rh.fBelow.fY - fBelow.fY) * (fBelow.fX - fAbove.fX);
|
||||
// figure out which is quad, line
|
||||
// if cached data says line did not intersect quad, use top/bottom
|
||||
if (fVerb != SkPath::kLine_Verb ? noIntersect(rh) : rh.noIntersect(*this)) {
|
||||
return abCompare(fAbove, fBelow, rh.fAbove, rh.fBelow);
|
||||
}
|
||||
// use whichever of top/tangent tangent/bottom overlaps more
|
||||
// with line top/bot
|
||||
// assumes quad/cubic can already be upconverted to cubic/cubic
|
||||
const SkPoint* line[2];
|
||||
const SkPoint* curve[4];
|
||||
if (fVerb != SkPath::kLine_Verb) {
|
||||
line[0] = &rh.fAbove;
|
||||
line[1] = &rh.fBelow;
|
||||
curve[0] = &fAbove;
|
||||
curve[1] = &fTangent;
|
||||
curve[2] = &fBelow;
|
||||
} else {
|
||||
line[0] = &fAbove;
|
||||
line[1] = &fBelow;
|
||||
curve[0] = &rh.fAbove;
|
||||
curve[1] = &rh.fTangent;
|
||||
curve[2] = &rh.fBelow;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool abCompare(const SkPoint& a1, const SkPoint& a2, const SkPoint& b1,
|
||||
const SkPoint& b2) const {
|
||||
double topD = a1.fX - b1.fX;
|
||||
if (b1.fY < a1.fY) {
|
||||
topD = (b2.fY - b1.fY) * topD - (a1.fY - b1.fY) * (b2.fX - b1.fX);
|
||||
} else if (b1.fY > a1.fY) {
|
||||
topD = (a2.fY - a1.fY) * topD + (b1.fY - a1.fY) * (a2.fX - a1.fX);
|
||||
}
|
||||
double botD = a2.fX - b2.fX;
|
||||
if (b2.fY > a2.fY) {
|
||||
botD = (b2.fY - b1.fY) * botD - (a2.fY - b2.fY) * (b2.fX - b1.fX);
|
||||
} else if (b2.fY < a2.fY) {
|
||||
botD = (a2.fY - a1.fY) * botD + (b2.fY - a2.fY) * (a2.fX - a1.fX);
|
||||
}
|
||||
// return sign of greater absolute value
|
||||
return (fabs(topD) > fabs(botD) ? topD : botD) < 0;
|
||||
@ -1477,6 +1504,37 @@ public:
|
||||
SkASSERT(!fExplicitTs);
|
||||
fTIndex = fTs->count() + 1;
|
||||
}
|
||||
|
||||
void calcAboveBelow(double tAbove, double tBelow) {
|
||||
fVerb = fWorkEdge.verb();
|
||||
switch (fVerb) {
|
||||
case SkPath::kLine_Verb:
|
||||
LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
|
||||
LineXYAtT(fWorkEdge.fPts, tBelow, &fTangent);
|
||||
fBelow = fTangent;
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
// FIXME: put array in struct to avoid copy?
|
||||
SkPoint quad[3];
|
||||
QuadSubDivide(fWorkEdge.fPts, tAbove, tBelow, quad);
|
||||
fAbove = quad[0];
|
||||
fTangent = quad[0] != quad[1] ? quad[1] : quad[2];
|
||||
fBelow = quad[2];
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
SkPoint cubic[3];
|
||||
CubicSubDivide(fWorkEdge.fPts, tAbove, tBelow, cubic);
|
||||
fAbove = cubic[0];
|
||||
// FIXME: can't see how quad logic for how tangent is used
|
||||
// extends to cubic
|
||||
fTangent = cubic[0] != cubic[1] ? cubic[1]
|
||||
: cubic[0] != cubic[2] ? cubic[2] : cubic[3];
|
||||
fBelow = cubic[3];
|
||||
break;
|
||||
default:
|
||||
SkASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
void calcLeft(SkScalar y) {
|
||||
// OPTIMIZE: put a kDone_Verb at the end of the verb list?
|
||||
@ -1491,29 +1549,14 @@ public:
|
||||
}
|
||||
|
||||
void calcLeft() {
|
||||
void (*xyAtTFunc)(const SkPoint a[], double t, SkPoint* out);
|
||||
switch (fWorkEdge.verb()) {
|
||||
case SkPath::kLine_Verb:
|
||||
xyAtTFunc = LineXYAtT;
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
xyAtTFunc = QuadXYAtT;
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
xyAtTFunc = CubicXYAtT;
|
||||
break;
|
||||
default:
|
||||
SkASSERT(0);
|
||||
}
|
||||
// OPTIMIZATION: if fXAbove, fXBelow have already been computed
|
||||
// for the fTIndex, don't do it again
|
||||
// For identical x, this lets us know which edge is first.
|
||||
// If both edges have T values < 1, check x at next T (fXBelow).
|
||||
int add = (fTIndex <= fTs->count() - fExplicitTs) - 1;
|
||||
double tAbove = t(fTIndex + add);
|
||||
(*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
|
||||
double tBelow = t(fTIndex - ~add);
|
||||
(*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
|
||||
// OPTIMIZATION: if fAbove, fBelow have already been computed
|
||||
// for the fTIndex, don't do it again
|
||||
calcAboveBelow(tAbove, tBelow);
|
||||
// For identical x, this lets us know which edge is first.
|
||||
// If both edges have T values < 1, check x at next T (fBelow).
|
||||
SkASSERT(tAbove != tBelow);
|
||||
// FIXME: this can loop forever
|
||||
// need a break if we hit the end
|
||||
@ -1523,13 +1566,12 @@ public:
|
||||
add -= 1;
|
||||
SkASSERT(fTIndex + add >= 0);
|
||||
tAbove = t(fTIndex + add);
|
||||
(*xyAtTFunc)(fWorkEdge.fPts, tAbove, &fAbove);
|
||||
} else {
|
||||
add += 1;
|
||||
SkASSERT(fTIndex - ~add <= fTs->count() + 1);
|
||||
tBelow = t(fTIndex - ~add);
|
||||
(*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
|
||||
}
|
||||
calcAboveBelow(tAbove, tBelow);
|
||||
}
|
||||
fTAbove = tAbove;
|
||||
fTBelow = tBelow;
|
||||
@ -1541,28 +1583,17 @@ public:
|
||||
|
||||
void fixBelow() {
|
||||
if (fFixBelow) {
|
||||
switch (fWorkEdge.verb()) {
|
||||
case SkPath::kLine_Verb:
|
||||
LineXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
QuadXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
CubicXYAtT(fWorkEdge.fPts, nextT(), &fBelow);
|
||||
break;
|
||||
default:
|
||||
SkASSERT(0);
|
||||
}
|
||||
fTBelow = nextT();
|
||||
calcAboveBelow(fTAbove, fTBelow);
|
||||
fFixBelow = false;
|
||||
}
|
||||
}
|
||||
|
||||
void init(const InEdge* edge) {
|
||||
fWorkEdge.init(edge);
|
||||
fDone = false;
|
||||
initT();
|
||||
fBelow.fY = SK_ScalarMin;
|
||||
fDone = false;
|
||||
fYBottom = SK_ScalarMin;
|
||||
}
|
||||
|
||||
@ -1576,6 +1607,10 @@ public:
|
||||
// fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
|
||||
// but templated arrays don't allow returning a pointer to the end() element
|
||||
fTIndex = 0;
|
||||
if (!fDone) {
|
||||
fVerb = fWorkEdge.verb();
|
||||
}
|
||||
SkASSERT(fVerb > SkPath::kMove_Verb);
|
||||
}
|
||||
|
||||
// OPTIMIZATION: record if two edges are coincident when the are intersected
|
||||
@ -1586,13 +1621,10 @@ public:
|
||||
if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
|
||||
return false;
|
||||
}
|
||||
SkPath::Verb verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
|
||||
SkPath::Verb edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
|
||||
: edge->fWorkEdge.verb();
|
||||
if (verb != edgeVerb) {
|
||||
if (fVerb != edge->fVerb) {
|
||||
return false;
|
||||
}
|
||||
switch (verb) {
|
||||
switch (fVerb) {
|
||||
case SkPath::kLine_Verb:
|
||||
return true;
|
||||
default:
|
||||
@ -1607,13 +1639,18 @@ public:
|
||||
return fAbove == edge->fAbove && fBelow == edge->fBelow;
|
||||
}
|
||||
|
||||
SkPath::Verb lastVerb() const {
|
||||
return fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
|
||||
}
|
||||
// SkPath::Verb lastVerb() const {
|
||||
// return fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
|
||||
// }
|
||||
|
||||
const SkPoint* lastPoints() const {
|
||||
return fDone ? fWorkEdge.lastPoints() : fWorkEdge.points();
|
||||
}
|
||||
|
||||
bool noIntersect(const ActiveEdge& ) const {
|
||||
// incomplete
|
||||
return false;
|
||||
}
|
||||
|
||||
// The shortest close call edge should be moved into a position where
|
||||
// it contributes if the winding is transitioning to or from zero.
|
||||
@ -1654,8 +1691,8 @@ public:
|
||||
}
|
||||
|
||||
bool swapUnordered(const ActiveEdge* edge, SkScalar bottom) const {
|
||||
SkASSERT(lastVerb() != SkPath::kLine_Verb
|
||||
|| edge->lastVerb() != SkPath::kLine_Verb);
|
||||
SkASSERT(fVerb != SkPath::kLine_Verb
|
||||
|| edge->fVerb != SkPath::kLine_Verb);
|
||||
if (fDone || edge->fDone) {
|
||||
return false;
|
||||
}
|
||||
@ -1670,24 +1707,24 @@ public:
|
||||
double t1, t2, b1, b2;
|
||||
// This logic must be kept in sync with operator <
|
||||
if (edge->fAbove.fY < fAbove.fY) {
|
||||
t1 = (edge->fBelow.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
|
||||
t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fBelow.fX - edge->fAbove.fX);
|
||||
t1 = (edge->fTangent.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
|
||||
t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fTangent.fX - edge->fAbove.fX);
|
||||
} else if (edge->fAbove.fY > fAbove.fY) {
|
||||
t1 = (fBelow.fY - fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
|
||||
t2 = (fAbove.fY - edge->fAbove.fY) * (fBelow.fX - fAbove.fX);
|
||||
t1 = (fTangent.fY - fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
|
||||
t2 = (fAbove.fY - edge->fAbove.fY) * (fTangent.fX - fAbove.fX);
|
||||
} else {
|
||||
t1 = fAbove.fX;
|
||||
t2 = edge->fAbove.fX;
|
||||
}
|
||||
if (edge->fBelow.fY > fBelow.fY) {
|
||||
b1 = (edge->fBelow.fY - edge->fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
|
||||
b2 = (fBelow.fY - edge->fBelow.fY) * (edge->fBelow.fX - edge->fAbove.fX);
|
||||
} else if (edge->fBelow.fY < fBelow.fY) {
|
||||
b1 = (fBelow.fY - fAbove.fY) * (fBelow.fX - edge->fBelow.fX);
|
||||
b2 = (fBelow.fY - edge->fBelow.fY) * (fBelow.fX - fAbove.fX);
|
||||
if (edge->fTangent.fY > fTangent.fY) {
|
||||
b1 = (edge->fTangent.fY - edge->fAbove.fY) * (fTangent.fX - edge->fTangent.fX);
|
||||
b2 = (fTangent.fY - edge->fTangent.fY) * (edge->fTangent.fX - edge->fAbove.fX);
|
||||
} else if (edge->fTangent.fY < fTangent.fY) {
|
||||
b1 = (fTangent.fY - fAbove.fY) * (fTangent.fX - edge->fTangent.fX);
|
||||
b2 = (fTangent.fY - edge->fTangent.fY) * (fTangent.fX - fAbove.fX);
|
||||
} else {
|
||||
b1 = fBelow.fX;
|
||||
b2 = edge->fBelow.fX;
|
||||
b1 = fTangent.fX;
|
||||
b2 = edge->fTangent.fX;
|
||||
}
|
||||
if (fabs(t1 - t2) > fabs(b1 - b2)) {
|
||||
ulps = UlpsDiff(t1, t2);
|
||||
@ -1701,32 +1738,30 @@ public:
|
||||
if (ulps < 0 || ulps > 32) {
|
||||
return false;
|
||||
}
|
||||
SkPath::Verb verb = lastVerb();
|
||||
SkPath::Verb edgeVerb = edge->lastVerb();
|
||||
if (verb == SkPath::kLine_Verb && edgeVerb == SkPath::kLine_Verb) {
|
||||
if (fVerb == SkPath::kLine_Verb && edge->fVerb == SkPath::kLine_Verb) {
|
||||
return true;
|
||||
}
|
||||
if (verb != SkPath::kLine_Verb && edgeVerb != SkPath::kLine_Verb) {
|
||||
if (fVerb != SkPath::kLine_Verb && edge->fVerb != SkPath::kLine_Verb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double ts[2];
|
||||
bool isLine = true;
|
||||
bool curveQuad = true;
|
||||
if (verb == SkPath::kCubic_Verb) {
|
||||
if (fVerb == SkPath::kCubic_Verb) {
|
||||
ts[0] = (fTAbove * 2 + fTBelow) / 3;
|
||||
ts[1] = (fTAbove + fTBelow * 2) / 3;
|
||||
curveQuad = isLine = false;
|
||||
} else if (edgeVerb == SkPath::kCubic_Verb) {
|
||||
} else if (edge->fVerb == SkPath::kCubic_Verb) {
|
||||
ts[0] = (edge->fTAbove * 2 + edge->fTBelow) / 3;
|
||||
ts[1] = (edge->fTAbove + edge->fTBelow * 2) / 3;
|
||||
curveQuad = false;
|
||||
} else if (verb == SkPath::kQuad_Verb) {
|
||||
} else if (fVerb == SkPath::kQuad_Verb) {
|
||||
ts[0] = fTAbove;
|
||||
ts[1] = (fTAbove + fTBelow) / 2;
|
||||
isLine = false;
|
||||
} else {
|
||||
SkASSERT(edgeVerb == SkPath::kQuad_Verb);
|
||||
SkASSERT(edge->fVerb == SkPath::kQuad_Verb);
|
||||
ts[0] = edge->fTAbove;
|
||||
ts[1] = (edge->fTAbove + edge->fTBelow) / 2;
|
||||
}
|
||||
@ -1775,10 +1810,10 @@ private:
|
||||
// utility used only by swapUnordered
|
||||
void extractAboveBelow(ActiveEdge& extracted) const {
|
||||
SkPoint curve[4];
|
||||
switch (lastVerb()) {
|
||||
switch (fVerb) {
|
||||
case SkPath::kLine_Verb:
|
||||
extracted.fAbove = fAbove;
|
||||
extracted.fBelow = fBelow;
|
||||
extracted.fTangent = fTangent;
|
||||
return;
|
||||
case SkPath::kQuad_Verb:
|
||||
QuadSubDivide(lastPoints(), fTAbove, fTBelow, curve);
|
||||
@ -1790,19 +1825,21 @@ private:
|
||||
SkASSERT(0);
|
||||
}
|
||||
extracted.fAbove = curve[0];
|
||||
extracted.fBelow = curve[1];
|
||||
extracted.fTangent = curve[1];
|
||||
}
|
||||
|
||||
public:
|
||||
WorkEdge fWorkEdge;
|
||||
const SkTDArray<double>* fTs;
|
||||
SkPoint fAbove;
|
||||
SkPoint fTangent;
|
||||
SkPoint fBelow;
|
||||
double fTAbove; // OPTIMIZATION: only required if edge has quads or cubics
|
||||
double fTBelow;
|
||||
SkScalar fYBottom;
|
||||
int fCoincident;
|
||||
int fTIndex;
|
||||
SkPath::Verb fVerb;
|
||||
bool fSkip; // OPTIMIZATION: use bitfields?
|
||||
bool fCloseCall;
|
||||
bool fDone;
|
||||
@ -2253,17 +2290,16 @@ static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
|
||||
for (index = 1; index < edgeCount; ++index) {
|
||||
activePtr = nextPtr;
|
||||
nextPtr = edgeList[index];
|
||||
if (firstIndex != index - 1 && !activePtr->fSkip) {
|
||||
if (nextPtr->fWorkEdge.verb() == SkPath::kLine_Verb
|
||||
&& activePtr->isUnordered(nextPtr)) {
|
||||
start here;
|
||||
// swap the line with the curve
|
||||
// back up to the previous edge and retest
|
||||
SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
|
||||
SkASSERT(index > 1);
|
||||
index -= 2;
|
||||
continue;
|
||||
}
|
||||
if (firstIndex != index - 1 && activePtr->fVerb > SkPath::kLine_Verb
|
||||
&& nextPtr->fVerb == SkPath::kLine_Verb
|
||||
&& activePtr->isUnordered(nextPtr)) {
|
||||
// swap the line with the curve
|
||||
// back up to the previous edge and retest
|
||||
SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
|
||||
SkASSERT(index > 1);
|
||||
index -= 2;
|
||||
nextPtr = edgeList[index];
|
||||
continue;
|
||||
}
|
||||
bool closeCall = false;
|
||||
activePtr->fCoincident = firstIndex;
|
||||
@ -2444,9 +2480,9 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
|
||||
bool moreToDo, aboveBottom;
|
||||
do {
|
||||
double currentT = activePtr->t();
|
||||
uint8_t verb = wt.verb();
|
||||
const SkPoint* points = wt.fPts;
|
||||
double nextT;
|
||||
SkPath::Verb verb = activePtr->fVerb;
|
||||
do {
|
||||
nextT = activePtr->nextT();
|
||||
// FIXME: obtuse: want efficient way to say
|
||||
@ -2503,9 +2539,10 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
|
||||
// will use these values if they're still valid instead of
|
||||
// recomputing
|
||||
if (clipped[verb].fY > activePtr->fBelow.fY
|
||||
&& bottom >= activePtr->fBelow.fY ) {
|
||||
&& bottom >= activePtr->fBelow.fY
|
||||
&& verb == SkPath::kLine_Verb) {
|
||||
activePtr->fAbove = activePtr->fBelow;
|
||||
activePtr->fBelow = clipped[verb];
|
||||
activePtr->fBelow = activePtr->fTangent = clipped[verb];
|
||||
activePtr->fTAbove = activePtr->fTBelow < 1
|
||||
? activePtr->fTBelow : 0;
|
||||
activePtr->fTBelow = nextT;
|
||||
@ -2609,6 +2646,17 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
|
||||
// if a quadratic or cubic now has an intermediate T value, see if the Ts
|
||||
// on either side cause the Y values to monotonically increase. If not, split
|
||||
// the curve at the new T.
|
||||
|
||||
// try an alternate approach which does not split curves or stitch edges
|
||||
// (may still need adjustCoincident, though)
|
||||
// the idea is to output non-intersecting contours, then figure out their
|
||||
// respective winding contribution
|
||||
// each contour will need to know whether it is CW or CCW, and then whether
|
||||
// a ray from that contour hits any a contour that contains it. The ray can
|
||||
// move to the left and then arbitrarily move up or down (as long as it never
|
||||
// moves to the right) to find a reference sibling contour or containing
|
||||
// contour. If the contour is part of an intersection, the companion contour
|
||||
// that is part of the intersection can determine the containership.
|
||||
if (builder.containsCurves()) {
|
||||
currentPtr = edgeList.begin();
|
||||
SkTArray<InEdge> splits;
|
||||
|
101
experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp
Normal file
101
experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include "EdgeWalker_Test.h"
|
||||
#include "Intersection_Tests.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkCanvas.h"
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
static void* testSimplify4x4QuadraticsMain(void* data)
|
||||
{
|
||||
char pathStr[1024];
|
||||
bzero(pathStr, sizeof(pathStr));
|
||||
SkASSERT(data);
|
||||
State4& state = *(State4*) data;
|
||||
int ax = state.a & 0x03;
|
||||
int ay = state.a >> 2;
|
||||
int bx = state.b & 0x03;
|
||||
int by = state.b >> 2;
|
||||
int cx = state.c & 0x03;
|
||||
int cy = state.c >> 2;
|
||||
int dx = state.d & 0x03;
|
||||
int dy = state.d >> 2;
|
||||
for (int e = 0 ; e < 16; ++e) {
|
||||
int ex = e & 0x03;
|
||||
int ey = e >> 2;
|
||||
for (int f = e ; f < 16; ++f) {
|
||||
int fx = f & 0x03;
|
||||
int fy = f >> 2;
|
||||
for (int g = f ; g < 16; ++g) {
|
||||
int gx = g & 0x03;
|
||||
int gy = g >> 2;
|
||||
for (int h = g ; h < 16; ++h) {
|
||||
int hx = h & 0x03;
|
||||
int hy = h >> 2;
|
||||
SkPath path, out;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(ax, ay);
|
||||
path.quadTo(bx, by, cx, cy);
|
||||
path.lineTo(dx, dy);
|
||||
path.close();
|
||||
path.moveTo(ex, ey);
|
||||
path.lineTo(fx, fy);
|
||||
path.quadTo(gx, gy, hx, hy);
|
||||
path.close();
|
||||
if (1) { // gdb: set print elements 400
|
||||
char* str = pathStr;
|
||||
str += sprintf(str, " path.moveTo(%d, %d);\n", ax, ay);
|
||||
str += sprintf(str, " path.quadTo(%d, %d, %d, %d);\n", bx, by, cx, cy);
|
||||
str += sprintf(str, " path.lineTo(%d, %d);\n", dx, dy);
|
||||
str += sprintf(str, " path.close();\n");
|
||||
str += sprintf(str, " path.moveTo(%d, %d);\n", ex, ey);
|
||||
str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy);
|
||||
str += sprintf(str, " path.quadTo(%d, %d, %d, %d);\n", gx, gy, hx, hy);
|
||||
str += sprintf(str, " path.close();");
|
||||
}
|
||||
if (!testSimplify(path, true, out, state.bitmap, state.canvas)) {
|
||||
SkDebugf("*/\n{ SkPath::kWinding_FillType, %d, %d, %d, %d,"
|
||||
" %d, %d, %d, %d },\n/*\n", state.a, state.b, state.c, state.d,
|
||||
e, f, g, h);
|
||||
}
|
||||
path.setFillType(SkPath::kEvenOdd_FillType);
|
||||
if (!testSimplify(path, true, out, state.bitmap, state.canvas)) {
|
||||
SkDebugf("*/\n{ SkPath::kEvenOdd_FillType, %d, %d, %d, %d,"
|
||||
" %d, %d, %d, %d },\n/*\n", state.a, state.b, state.c, state.d,
|
||||
e, f, g, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int maxThreads = gRunTestsInOneThread ? 1 : 24;
|
||||
|
||||
void Simplify4x4QuadraticsThreaded_Test()
|
||||
{
|
||||
State4 threadState[maxThreads];
|
||||
int threadIndex = 0;
|
||||
for (int a = 0; a < 16; ++a) {
|
||||
for (int b = a ; b < 16; ++b) {
|
||||
for (int c = b ; c < 16; ++c) {
|
||||
for (int d = c; d < 16; ++d) {
|
||||
State4* statePtr = &threadState[threadIndex];
|
||||
statePtr->a = a;
|
||||
statePtr->b = b;
|
||||
statePtr->c = c;
|
||||
statePtr->d = d;
|
||||
if (maxThreads > 1) {
|
||||
createThread(statePtr, testSimplify4x4QuadraticsMain);
|
||||
if (++threadIndex >= maxThreads) {
|
||||
waitForCompletion(threadState, threadIndex);
|
||||
}
|
||||
} else {
|
||||
testSimplify4x4QuadraticsMain(statePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
waitForCompletion(threadState, threadIndex);
|
||||
}
|
@ -218,7 +218,22 @@ static void testSimplifyQuadratic16() {
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
|
||||
static void testSimplifyQuadratic17() {
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(2, 2);
|
||||
path.close();
|
||||
path.moveTo(0, 1);
|
||||
path.lineTo(0, 1);
|
||||
path.quadTo(2, 1, 3, 3);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
|
||||
static void (*simplifyTests[])() = {
|
||||
testSimplifyQuadratic17,
|
||||
testSimplifyQuadratic16,
|
||||
testSimplifyQuadratic15,
|
||||
testSimplifyQuadratic14,
|
||||
@ -239,7 +254,7 @@ static void (*simplifyTests[])() = {
|
||||
|
||||
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
|
||||
|
||||
static void (*firstTest)() = 0;
|
||||
static void (*firstTest)() = testSimplifyQuadratic14;
|
||||
static bool skipAll = false;
|
||||
|
||||
void SimplifyQuadraticPaths_Test() {
|
||||
|
@ -1,20 +1,18 @@
|
||||
#include "DataTypes.h"
|
||||
#include "Extrema.h"
|
||||
|
||||
static int valid_unit_divide(double numer, double denom, double* ratio)
|
||||
static int validUnitDivide(double numer, double denom, double* ratio)
|
||||
{
|
||||
if (numer < 0)
|
||||
{
|
||||
if (numer < 0) {
|
||||
numer = -numer;
|
||||
denom = -denom;
|
||||
}
|
||||
|
||||
if (denom == 0 || numer == 0 || numer >= denom)
|
||||
return 0;
|
||||
|
||||
double r = numer / denom;
|
||||
if (r == 0) // catch underflow if numer <<<< denom
|
||||
if (r == 0) { // catch underflow if numer <<<< denom
|
||||
return 0;
|
||||
}
|
||||
*ratio = r;
|
||||
return 1;
|
||||
}
|
||||
@ -25,10 +23,10 @@ static int valid_unit_divide(double numer, double denom, double* ratio)
|
||||
x1 = Q / A
|
||||
x2 = C / Q
|
||||
*/
|
||||
static int SkFindUnitQuadRoots(double A, double B, double C, double roots[2])
|
||||
static int findUnitQuadRoots(double A, double B, double C, double roots[2])
|
||||
{
|
||||
if (A == 0)
|
||||
return valid_unit_divide(-C, B, roots);
|
||||
return validUnitDivide(-C, B, roots);
|
||||
|
||||
double* r = roots;
|
||||
|
||||
@ -39,8 +37,8 @@ static int SkFindUnitQuadRoots(double A, double B, double C, double roots[2])
|
||||
R = sqrt(R);
|
||||
|
||||
double Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
|
||||
r += valid_unit_divide(Q, A, r);
|
||||
r += valid_unit_divide(C, Q, r);
|
||||
r += validUnitDivide(Q, A, r);
|
||||
r += validUnitDivide(C, Q, r);
|
||||
if (r - roots == 2 && approximately_equal(roots[0], roots[1])) { // nearly-equal?
|
||||
r -= 1; // skip the double root
|
||||
}
|
||||
@ -51,16 +49,16 @@ static int SkFindUnitQuadRoots(double A, double B, double C, double roots[2])
|
||||
A = 3(-a + 3(b - c) + d)
|
||||
B = 6(a - 2b + c)
|
||||
C = 3(b - a)
|
||||
Solve for t, keeping only those that fit betwee 0 < t < 1
|
||||
Solve for t, keeping only those that fit between 0 < t < 1
|
||||
*/
|
||||
int SkFindCubicExtrema(double a, double b, double c, double d, double tValues[2])
|
||||
int findExtrema(double a, double b, double c, double d, double tValues[2])
|
||||
{
|
||||
// we divide A,B,C by 3 to simplify
|
||||
double A = d - a + 3*(b - c);
|
||||
double B = 2*(a - b - b + c);
|
||||
double C = b - a;
|
||||
|
||||
return SkFindUnitQuadRoots(A, B, C, tValues);
|
||||
return findUnitQuadRoots(A, B, C, tValues);
|
||||
}
|
||||
|
||||
/** Quad'(t) = At + B, where
|
||||
@ -68,10 +66,10 @@ int SkFindCubicExtrema(double a, double b, double c, double d, double tValues[2]
|
||||
B = 2(b - a)
|
||||
Solve for t, only if it fits between 0 < t < 1
|
||||
*/
|
||||
int SkFindQuadExtrema(double a, double b, double c, double tValue[1])
|
||||
int findExtrema(double a, double b, double c, double tValue[1])
|
||||
{
|
||||
/* At + B == 0
|
||||
t = -B / A
|
||||
*/
|
||||
return valid_unit_divide(a - b, a - b - b + c, tValue);
|
||||
return validUnitDivide(a - b, a - b - b + c, tValue);
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
int SkFindCubicExtrema(double a, double b, double c, double d, double tValues[2]);
|
||||
int SkFindQuadExtrema(double a, double b, double c, double tValue[1]);
|
||||
int findExtrema(double a, double b, double c, double d, double tValues[2]);
|
||||
int findExtrema(double a, double b, double c, double tValue[1]);
|
||||
|
@ -4,12 +4,10 @@
|
||||
void cubecode_test(int test);
|
||||
void testSimplify();
|
||||
|
||||
#define TEST_QUADS_FIRST 01
|
||||
#define TEST_QUADS_FIRST 0
|
||||
|
||||
void Intersection_Tests() {
|
||||
#if !TEST_QUADS_FIRST
|
||||
ActiveEdge_Test();
|
||||
#endif
|
||||
SimplifyAddIntersectingTs_Test();
|
||||
|
||||
cubecode_test(1);
|
||||
convert_testx();
|
||||
@ -29,8 +27,8 @@ void Intersection_Tests() {
|
||||
SimplifyRectangularPaths_Test();
|
||||
SimplifyQuadralateralPaths_Test();
|
||||
|
||||
#if TEST_QUADS_FIRST
|
||||
ActiveEdge_Test();
|
||||
#if TEST_QUADS_FIRST
|
||||
Simplify4x4QuadraticsThreaded_Test();
|
||||
#endif
|
||||
SimplifyDegenerate4x4TrianglesThreaded_Test();
|
||||
|
@ -12,6 +12,7 @@ void LineCubicIntersection_Test();
|
||||
void LineIntersection_Test();
|
||||
void LineParameter_Test();
|
||||
void LineQuadraticIntersection_Test();
|
||||
void SimplifyAddIntersectingTs_Test();
|
||||
void SimplifyDegenerate4x4TrianglesThreaded_Test();
|
||||
void SimplifyNondegenerate4x4TrianglesThreaded_Test();
|
||||
void SimplifyPolygonPaths_Test();
|
||||
|
@ -109,6 +109,13 @@ int horizontalIntersect(double axisIntercept) {
|
||||
return cubicRoots(A, B, C, D, range);
|
||||
}
|
||||
|
||||
int verticalIntersect(double axisIntercept) {
|
||||
double A, B, C, D;
|
||||
coefficients(&cubic[0].x, A, B, C, D);
|
||||
D -= axisIntercept;
|
||||
return cubicRoots(A, B, C, D, range);
|
||||
}
|
||||
|
||||
double findLineT(double t) {
|
||||
const double* cPtr;
|
||||
const double* lPtr;
|
||||
@ -158,6 +165,56 @@ int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
|
||||
return result;
|
||||
}
|
||||
|
||||
int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
|
||||
bool flipped, Intersections& intersections) {
|
||||
LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]);
|
||||
int result = c.horizontalIntersect(y);
|
||||
for (int index = 0; index < result; ) {
|
||||
double x, y;
|
||||
xy_at_t(cubic, intersections.fT[0][index], x, y);
|
||||
if (x < left || x > right) {
|
||||
if (--result > index) {
|
||||
intersections.fT[0][index] = intersections.fT[0][result];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
intersections.fT[0][index] = (x - left) / (right - left);
|
||||
++index;
|
||||
}
|
||||
if (flipped) {
|
||||
// OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
|
||||
for (int index = 0; index < result; ++index) {
|
||||
intersections.fT[1][index] = 1 - intersections.fT[1][index];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int verticalIntersect(const Cubic& cubic, double top, double bottom, double x,
|
||||
bool flipped, Intersections& intersections) {
|
||||
LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]);
|
||||
int result = c.verticalIntersect(x);
|
||||
for (int index = 0; index < result; ) {
|
||||
double x, y;
|
||||
xy_at_t(cubic, intersections.fT[0][index], x, y);
|
||||
if (y < top || y > bottom) {
|
||||
if (--result > index) {
|
||||
intersections.fT[0][index] = intersections.fT[0][result];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
intersections.fT[0][index] = (y - top) / (bottom - top);
|
||||
++index;
|
||||
}
|
||||
if (flipped) {
|
||||
// OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
|
||||
for (int index = 0; index < result; ++index) {
|
||||
intersections.fT[1][index] = 1 - intersections.fT[1][index];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]) {
|
||||
LineCubicIntersections c(cubic, line, cRange);
|
||||
int roots;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "DataTypes.h"
|
||||
#include "Intersections.h"
|
||||
#include "LineIntersection.h"
|
||||
#include <algorithm> // used for std::swap
|
||||
|
||||
@ -112,9 +113,9 @@ int horizontalLineIntersect(const _Line& line, double left, double right,
|
||||
double y, double tRange[2]) {
|
||||
int result = horizontalIntersect(line, y, tRange);
|
||||
if (result != 1) {
|
||||
// FIXME: this is incorrect if result == 2
|
||||
return result;
|
||||
}
|
||||
// FIXME: this is incorrect if result == 2
|
||||
double xIntercept = line[0].x + tRange[0] * (line[1].x - line[0].x);
|
||||
if (xIntercept > right || xIntercept < left) {
|
||||
return 0;
|
||||
@ -122,6 +123,116 @@ int horizontalLineIntersect(const _Line& line, double left, double right,
|
||||
return result;
|
||||
}
|
||||
|
||||
int horizontalIntersect(const _Line& line, double left, double right,
|
||||
double y, bool flipped, Intersections& intersections) {
|
||||
int result = horizontalIntersect(line, y, intersections.fT[0]);
|
||||
switch (result) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: {
|
||||
double xIntercept = line[0].x + intersections.fT[0][0]
|
||||
* (line[1].x - line[0].x);
|
||||
if (xIntercept > right || xIntercept < left) {
|
||||
return 0;
|
||||
}
|
||||
intersections.fT[1][0] = (xIntercept - left) / (right - left);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
double lineL = line[0].x;
|
||||
double lineR = line[1].x;
|
||||
if (lineL > lineR) {
|
||||
std::swap(lineL, lineR);
|
||||
}
|
||||
double overlapL = std::max(left, lineL);
|
||||
double overlapR = std::min(right, lineR);
|
||||
if (overlapL > overlapR) {
|
||||
return 0;
|
||||
}
|
||||
if (overlapL == overlapR) {
|
||||
result = 1;
|
||||
}
|
||||
intersections.fT[0][0] = (overlapL - line[0].x) / (line[1].x - line[0].x);
|
||||
intersections.fT[1][0] = (overlapL - left) / (right - left);
|
||||
if (result > 1) {
|
||||
intersections.fT[0][1] = (overlapR - line[0].x) / (line[1].x - line[0].x);
|
||||
intersections.fT[1][1] = (overlapR - left) / (right - left);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (flipped) {
|
||||
// OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
|
||||
for (int index = 0; index < result; ++index) {
|
||||
intersections.fT[1][index] = 1 - intersections.fT[1][index];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int verticalIntersect(const _Line& line, double x, double tRange[2]) {
|
||||
double min = line[0].x;
|
||||
double max = line[1].x;
|
||||
if (min > max) {
|
||||
std::swap(min, max);
|
||||
}
|
||||
if (min > x || max < x) {
|
||||
return 0;
|
||||
}
|
||||
if (approximately_equal(min, max)) {
|
||||
tRange[0] = 0;
|
||||
tRange[1] = 1;
|
||||
return 2;
|
||||
}
|
||||
tRange[0] = (x - line[0].x) / (line[1].x - line[0].x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int verticalIntersect(const _Line& line, double top, double bottom,
|
||||
double x, bool flipped, Intersections& intersections) {
|
||||
int result = verticalIntersect(line, x, intersections.fT[0]);
|
||||
switch (result) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: {
|
||||
double yIntercept = line[0].y + intersections.fT[0][0]
|
||||
* (line[1].y - line[0].y);
|
||||
if (yIntercept > bottom || yIntercept < top) {
|
||||
return 0;
|
||||
}
|
||||
intersections.fT[1][0] = (yIntercept - top) / (bottom - top);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
double lineT = line[0].y;
|
||||
double lineB = line[1].y;
|
||||
if (lineT > lineB) {
|
||||
std::swap(lineT, lineB);
|
||||
}
|
||||
double overlapT = std::max(top, lineT);
|
||||
double overlapB = std::min(bottom, lineB);
|
||||
if (overlapT > overlapB) {
|
||||
return 0;
|
||||
}
|
||||
if (overlapT == overlapB) {
|
||||
result = 1;
|
||||
}
|
||||
intersections.fT[0][0] = (overlapT - line[0].y) / (line[1].y - line[0].y);
|
||||
intersections.fT[1][0] = (overlapT - top) / (bottom - top);
|
||||
if (result > 1) {
|
||||
intersections.fT[0][1] = (overlapB - line[0].y) / (line[1].y - line[0].y);
|
||||
intersections.fT[1][1] = (overlapB - top) / (bottom - top);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (flipped) {
|
||||
// OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
|
||||
for (int index = 0; index < result; ++index) {
|
||||
intersections.fT[1][index] = 1 - intersections.fT[1][index];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
|
||||
// 4 subs, 2 muls, 1 cmp
|
||||
static bool ccw(const _Point& A, const _Point& B, const _Point& C) {
|
||||
|
@ -6,6 +6,8 @@
|
||||
int horizontalIntersect(const _Line& line, double y, double tRange[2]);
|
||||
int horizontalLineIntersect(const _Line& line, double left, double right,
|
||||
double y, double tRange[2]);
|
||||
int verticalLineIntersect(const _Line& line, double top, double bottom,
|
||||
double x, double tRange[2]);
|
||||
int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]);
|
||||
bool testIntersect(const _Line& a, const _Line& b);
|
||||
|
||||
|
@ -141,6 +141,16 @@ int horizontalIntersect(double axisIntercept) {
|
||||
return quadraticRoots(D, E, F, intersections.fT[0]);
|
||||
}
|
||||
|
||||
int verticalIntersect(double axisIntercept) {
|
||||
double D = quad[2].x; // f
|
||||
double E = quad[1].x; // e
|
||||
double F = quad[0].x; // 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:
|
||||
|
||||
double findLineT(double t) {
|
||||
@ -167,6 +177,46 @@ bool moreHorizontal;
|
||||
|
||||
};
|
||||
|
||||
// utility for pairs of coincident quads
|
||||
static double horizontalIntersect(const Quadratic& quad, const _Point& pt) {
|
||||
Intersections intersections;
|
||||
LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
|
||||
int result = q.horizontalIntersect(pt.y);
|
||||
if (result == 0) {
|
||||
return -1;
|
||||
}
|
||||
assert(result == 1);
|
||||
double x, y;
|
||||
xy_at_t(quad, intersections.fT[0][0], x, y);
|
||||
if (approximately_equal(x, pt.x)) {
|
||||
return intersections.fT[0][0];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static double verticalIntersect(const Quadratic& quad, const _Point& pt) {
|
||||
Intersections intersections;
|
||||
LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
|
||||
int result = q.horizontalIntersect(pt.x);
|
||||
if (result == 0) {
|
||||
return -1;
|
||||
}
|
||||
assert(result == 1);
|
||||
double x, y;
|
||||
xy_at_t(quad, intersections.fT[0][0], x, y);
|
||||
if (approximately_equal(y, pt.y)) {
|
||||
return intersections.fT[0][0];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
double axialIntersect(const Quadratic& q1, const _Point& p, bool vertical) {
|
||||
if (vertical) {
|
||||
return verticalIntersect(q1, p);
|
||||
}
|
||||
return horizontalIntersect(q1, p);
|
||||
}
|
||||
|
||||
int horizontalIntersect(const Quadratic& quad, double left, double right,
|
||||
double y, double tRange[2]) {
|
||||
Intersections i;
|
||||
@ -184,6 +234,56 @@ int horizontalIntersect(const Quadratic& quad, double left, double right,
|
||||
return tCount;
|
||||
}
|
||||
|
||||
int horizontalIntersect(const Quadratic& quad, double left, double right, double y,
|
||||
bool flipped, Intersections& intersections) {
|
||||
LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
|
||||
int result = q.horizontalIntersect(y);
|
||||
for (int index = 0; index < result; ) {
|
||||
double x, y;
|
||||
xy_at_t(quad, intersections.fT[0][index], x, y);
|
||||
if (x < left || x > right) {
|
||||
if (--result > index) {
|
||||
intersections.fT[0][index] = intersections.fT[0][result];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
intersections.fT[0][index] = (x - left) / (right - left);
|
||||
++index;
|
||||
}
|
||||
if (flipped) {
|
||||
// OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
|
||||
for (int index = 0; index < result; ++index) {
|
||||
intersections.fT[1][index] = 1 - intersections.fT[1][index];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int verticalIntersect(const Quadratic& quad, double top, double bottom, double x,
|
||||
bool flipped, Intersections& intersections) {
|
||||
LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
|
||||
int result = q.verticalIntersect(x);
|
||||
for (int index = 0; index < result; ) {
|
||||
double x, y;
|
||||
xy_at_t(quad, intersections.fT[0][index], x, y);
|
||||
if (y < top || y > bottom) {
|
||||
if (--result > index) {
|
||||
intersections.fT[0][index] = intersections.fT[0][result];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
intersections.fT[0][index] = (y - top) / (bottom - top);
|
||||
++index;
|
||||
}
|
||||
if (flipped) {
|
||||
// OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
|
||||
for (int index = 0; index < result; ++index) {
|
||||
intersections.fT[1][index] = 1 - intersections.fT[1][index];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool intersect(const Quadratic& quad, const _Line& line, Intersections& i) {
|
||||
LineQuadraticIntersections q(quad, line, i);
|
||||
return q.intersect();
|
||||
|
@ -8,7 +8,9 @@ struct lineQuad {
|
||||
Quadratic quad;
|
||||
_Line line;
|
||||
} lineQuadTests[] = {
|
||||
{{{0, 0}, {0, 1}, {1, 1}}, {{0, 1}, {1, 0}}}
|
||||
{{{2, 0}, {1, 1}, {2, 2}}, {{0, 0}, {0, 2}}},
|
||||
{{{4, 0}, {0, 1}, {4, 2}}, {{3, 1}, {4, 1}}},
|
||||
{{{0, 0}, {0, 1}, {1, 1}}, {{0, 1}, {1, 0}}},
|
||||
};
|
||||
|
||||
size_t lineQuadTests_count = sizeof(lineQuadTests) / sizeof(lineQuadTests[0]);
|
||||
|
@ -30,3 +30,28 @@ void sub_divide(const _Line& line, double t1, double t2, _Line& dst) {
|
||||
dst[1].x = line[0].x - t2 * delta.x;
|
||||
dst[1].y = line[0].y - t2 * delta.y;
|
||||
}
|
||||
|
||||
// may have this below somewhere else already:
|
||||
// copying here because I thought it was clever
|
||||
|
||||
// Copyright 2001, softSurfer (www.softsurfer.com)
|
||||
// This code may be freely used and modified for any purpose
|
||||
// providing that this copyright notice is included with it.
|
||||
// SoftSurfer makes no warranty for this code, and cannot be held
|
||||
// liable for any real or imagined damage resulting from its use.
|
||||
// Users of this code must verify correctness for their application.
|
||||
|
||||
// Assume that a class is already given for the object:
|
||||
// Point with coordinates {float x, y;}
|
||||
//===================================================================
|
||||
|
||||
// isLeft(): tests if a point is Left|On|Right of an infinite line.
|
||||
// Input: three points P0, P1, and P2
|
||||
// Return: >0 for P2 left of the line through P0 and P1
|
||||
// =0 for P2 on the line
|
||||
// <0 for P2 right of the line
|
||||
// See: the January 2001 Algorithm on Area of Triangles
|
||||
float isLeft( _Point P0, _Point P1, _Point P2 )
|
||||
{
|
||||
return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y);
|
||||
}
|
||||
|
46
experimental/Intersection/QuadraticBounds.cpp
Normal file
46
experimental/Intersection/QuadraticBounds.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "DataTypes.h"
|
||||
#include "Extrema.h"
|
||||
|
||||
static int isBoundedByEndPoints(double a, double b, double c)
|
||||
{
|
||||
return (a <= b && b <= c) || (a >= b && b >= c);
|
||||
}
|
||||
|
||||
double leftMostT(const Quadratic& quad, double startT, double endT) {
|
||||
double leftT;
|
||||
if (findExtrema(quad[0].x, quad[1].x, quad[2].x, &leftT)
|
||||
&& startT <= leftT && leftT <= endT) {
|
||||
return leftT;
|
||||
}
|
||||
_Point startPt;
|
||||
xy_at_t(quad, startT, startPt.x, startPt.y);
|
||||
_Point endPt;
|
||||
xy_at_t(quad, endT, endPt.x, endPt.y);
|
||||
return startPt.x <= endPt.x ? startT : endT;
|
||||
}
|
||||
|
||||
void _Rect::setBounds(const Quadratic& quad) {
|
||||
set(quad[0]);
|
||||
add(quad[2]);
|
||||
double tValues[2];
|
||||
int roots = 0;
|
||||
if (!isBoundedByEndPoints(quad[0].x, quad[1].x, quad[2].x)) {
|
||||
roots = findExtrema(quad[0].x, quad[1].x, quad[2].x, tValues);
|
||||
}
|
||||
if (!isBoundedByEndPoints(quad[0].y, quad[1].y, quad[2].y)) {
|
||||
roots += findExtrema(quad[0].y, quad[1].y, quad[2].y,
|
||||
&tValues[roots]);
|
||||
}
|
||||
for (int x = 0; x < roots; ++x) {
|
||||
_Point result;
|
||||
xy_at_t(quad, tValues[x], result.x, result.y);
|
||||
add(result);
|
||||
}
|
||||
}
|
||||
|
||||
void _Rect::setRawBounds(const Quadratic& quad) {
|
||||
set(quad[0]);
|
||||
for (int x = 1; x < 3; ++x) {
|
||||
add(quad[x]);
|
||||
}
|
||||
}
|
@ -97,7 +97,6 @@ bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
|
||||
double newMinT2 = interp(minT2, maxT2, minT);
|
||||
double newMaxT2 = interp(minT2, maxT2, maxT);
|
||||
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,
|
||||
splits, newMinT2, newMaxT2, minT2, maxT2, split);
|
||||
@ -144,6 +143,35 @@ int splits;
|
||||
};
|
||||
|
||||
bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
|
||||
if (implicit_matches(q1, q2)) {
|
||||
// FIXME: compute T values
|
||||
// compute the intersections of the ends to find the coincident span
|
||||
bool useVertical = fabs(q1[0].x - q1[2].x) < fabs(q1[0].y - q1[2].y);
|
||||
double t;
|
||||
if ((t = axialIntersect(q1, q2[0], useVertical)) >= 0) {
|
||||
i.fT[0][0] = t;
|
||||
i.fT[1][0] = 0;
|
||||
i.fUsed++;
|
||||
}
|
||||
if ((t = axialIntersect(q1, q2[2], useVertical)) >= 0) {
|
||||
i.fT[0][i.fUsed] = t;
|
||||
i.fT[1][i.fUsed] = 1;
|
||||
i.fUsed++;
|
||||
}
|
||||
useVertical = fabs(q2[0].x - q2[2].x) < fabs(q2[0].y - q2[2].y);
|
||||
if ((t = axialIntersect(q2, q1[0], useVertical)) >= 0) {
|
||||
i.fT[0][i.fUsed] = 0;
|
||||
i.fT[1][i.fUsed] = t;
|
||||
i.fUsed++;
|
||||
}
|
||||
if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) {
|
||||
i.fT[0][i.fUsed] = 1;
|
||||
i.fT[1][i.fUsed] = t;
|
||||
i.fUsed++;
|
||||
}
|
||||
assert(i.fUsed <= 2);
|
||||
return i.fUsed > 0;
|
||||
}
|
||||
QuadraticIntersections q(q1, q2, i);
|
||||
return q.intersect();
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ static int vertical_line(const Quadratic& quad, Quadratic& reduction) {
|
||||
reduction[1] = quad[2];
|
||||
int smaller = reduction[1].y > reduction[0].y;
|
||||
int larger = smaller ^ 1;
|
||||
if (SkFindQuadExtrema(quad[0].y, quad[1].y, quad[2].y, &tValue)) {
|
||||
if (findExtrema(quad[0].y, quad[1].y, quad[2].y, &tValue)) {
|
||||
double yExtrema = interp_quad_coords(quad[0].y, quad[1].y, quad[2].y, tValue);
|
||||
if (reduction[smaller].y > yExtrema) {
|
||||
reduction[smaller].y = yExtrema;
|
||||
@ -38,7 +38,7 @@ static int horizontal_line(const Quadratic& quad, Quadratic& reduction) {
|
||||
reduction[1] = quad[2];
|
||||
int smaller = reduction[1].x > reduction[0].x;
|
||||
int larger = smaller ^ 1;
|
||||
if (SkFindQuadExtrema(quad[0].x, quad[1].x, quad[2].x, &tValue)) {
|
||||
if (findExtrema(quad[0].x, quad[1].x, quad[2].x, &tValue)) {
|
||||
double xExtrema = interp_quad_coords(quad[0].x, quad[1].x, quad[2].x, tValue);
|
||||
if (reduction[smaller].x > xExtrema) {
|
||||
reduction[smaller].x = xExtrema;
|
||||
@ -85,9 +85,9 @@ static int check_linear(const Quadratic& quad, Quadratic& reduction,
|
||||
double tValue;
|
||||
int root;
|
||||
if (useX) {
|
||||
root = SkFindQuadExtrema(quad[0].x, quad[1].x, quad[2].x, &tValue);
|
||||
root = findExtrema(quad[0].x, quad[1].x, quad[2].x, &tValue);
|
||||
} else {
|
||||
root = SkFindQuadExtrema(quad[0].y, quad[1].y, quad[2].y, &tValue);
|
||||
root = findExtrema(quad[0].y, quad[1].y, quad[2].y, &tValue);
|
||||
}
|
||||
if (root) {
|
||||
_Point extrema;
|
||||
@ -146,8 +146,8 @@ int reduceOrder(const Quadratic& quad, Quadratic& reduction) {
|
||||
minYSet |= 1 << index;
|
||||
}
|
||||
}
|
||||
if (minXSet == 0xF) { // test for vertical line
|
||||
if (minYSet == 0xF) { // return 1 if all four are coincident
|
||||
if (minXSet == 0x7) { // test for vertical line
|
||||
if (minYSet == 0x7) { // return 1 if all four are coincident
|
||||
return coincident_line(quad, reduction);
|
||||
}
|
||||
return vertical_line(quad, reduction);
|
||||
|
@ -25,5 +25,3 @@ int quadraticRoots(double A, double B, double C, double t[2]) {
|
||||
}
|
||||
return foundRoots;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,44 +0,0 @@
|
||||
#include "DataTypes.h"
|
||||
#include "Extrema.h"
|
||||
|
||||
void _Rect::setBounds(const Cubic& cubic) {
|
||||
set(cubic[0]);
|
||||
add(cubic[3]);
|
||||
double tValues[4];
|
||||
int roots = SkFindCubicExtrema(cubic[0].x, cubic[1].x, cubic[2].x,
|
||||
cubic[3].x, tValues);
|
||||
roots += SkFindCubicExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y,
|
||||
&tValues[roots]);
|
||||
for (int x = 0; x < roots; ++x) {
|
||||
_Point result;
|
||||
xy_at_t(cubic, tValues[x], result.x, result.y);
|
||||
add(result);
|
||||
}
|
||||
}
|
||||
|
||||
void _Rect::setRawBounds(const Cubic& cubic) {
|
||||
set(cubic[0]);
|
||||
for (int x = 1; x < 4; ++x) {
|
||||
add(cubic[x]);
|
||||
}
|
||||
}
|
||||
|
||||
void _Rect::setBounds(const Quadratic& quad) {
|
||||
set(quad[0]);
|
||||
add(quad[2]);
|
||||
double tValues[2];
|
||||
int roots = SkFindQuadExtrema(quad[0].x, quad[1].x, quad[2].x, tValues);
|
||||
roots += SkFindQuadExtrema(quad[0].y, quad[1].y, quad[2].y, &tValues[roots]);
|
||||
for (int x = 0; x < roots; ++x) {
|
||||
_Point result;
|
||||
xy_at_t(quad, tValues[x], result.x, result.y);
|
||||
add(result);
|
||||
}
|
||||
}
|
||||
|
||||
void _Rect::setRawBounds(const Quadratic& quad) {
|
||||
set(quad[0]);
|
||||
for (int x = 1; x < 3; ++x) {
|
||||
add(quad[x]);
|
||||
}
|
||||
}
|
1213
experimental/Intersection/Simplify.cpp
Normal file
1213
experimental/Intersection/Simplify.cpp
Normal file
File diff suppressed because it is too large
Load Diff
132
experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp
Normal file
132
experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
#include "CurveIntersection.h"
|
||||
#include "Intersections.h"
|
||||
#include "LineIntersection.h"
|
||||
#include "SkPath.h"
|
||||
#include "SkRect.h"
|
||||
#include "SkTArray.h"
|
||||
#include "SkTDArray.h"
|
||||
#include "ShapeOps.h"
|
||||
#include "TSearch.h"
|
||||
|
||||
namespace SimplifyAddIntersectingTsTest {
|
||||
|
||||
#include "Simplify.cpp"
|
||||
|
||||
} // end of SimplifyAddIntersectingTsTest namespace
|
||||
|
||||
#include "Intersection_Tests.h"
|
||||
|
||||
static const SkPoint lines[][2] = {
|
||||
{{ 1, 1}, { 1, 1}}, // degenerate
|
||||
{{ 1, 1}, { 4, 1}}, // horizontal
|
||||
{{ 4, 1}, { 9, 1}},
|
||||
{{ 2, 1}, { 3, 1}},
|
||||
{{ 2, 1}, { 6, 1}},
|
||||
{{ 5, 1}, { 9, 1}},
|
||||
{{ 1, 1}, { 1, 4}}, // vertical
|
||||
{{ 1, 2}, { 1, 3}},
|
||||
{{ 1, 2}, { 1, 6}},
|
||||
{{ 1, 5}, { 1, 9}},
|
||||
{{ 1, 1}, { 3, 3}}, // diagonal
|
||||
{{ 2, 2}, { 4, 4}},
|
||||
{{ 2, 4}, { 4, 2}},
|
||||
};
|
||||
|
||||
static const size_t lineCount = sizeof(lines) / sizeof(lines[0]);
|
||||
|
||||
static const SkPoint quads[][3] = {
|
||||
{{ 1, 1}, { 1, 1}, { 1, 1}}, // degenerate
|
||||
{{ 1, 1}, { 4, 1}, { 5, 1}}, // line
|
||||
{{ 1, 1}, { 4, 1}, { 4, 4}}, // curve
|
||||
};
|
||||
|
||||
static const size_t quadCount = sizeof(quads) / sizeof(quads[0]);
|
||||
|
||||
static const SkPoint cubics[][4] = {
|
||||
{{ 1, 1}, { 1, 1}, { 1, 1}, { 1, 1}}, // degenerate
|
||||
{{ 1, 1}, { 4, 1}, { 5, 1}, { 6, 1}}, // line
|
||||
{{ 1, 1}, { 3, 1}, { 4, 2}, { 4, 4}}, // curve
|
||||
};
|
||||
|
||||
static const size_t cubicCount = sizeof(cubics) / sizeof(cubics[0]);
|
||||
static const size_t testCount = lineCount + quadCount + cubicCount;
|
||||
|
||||
static SkPath::Verb setPath(int outer, SkPath& path, const SkPoint*& pts1) {
|
||||
SkPath::Verb c1Type;
|
||||
if (outer < lineCount) {
|
||||
path.moveTo(lines[outer][0].fX, lines[outer][0].fY);
|
||||
path.lineTo(lines[outer][1].fX, lines[outer][1].fY);
|
||||
c1Type = SkPath::kLine_Verb;
|
||||
pts1 = lines[outer];
|
||||
} else {
|
||||
outer -= lineCount;
|
||||
if (outer < quadCount) {
|
||||
path.moveTo(quads[outer][0].fX, quads[outer][0].fY);
|
||||
path.quadTo(quads[outer][1].fX, quads[outer][1].fY,
|
||||
quads[outer][2].fX, quads[outer][2].fY);
|
||||
c1Type = SkPath::kQuad_Verb;
|
||||
pts1 = quads[outer];
|
||||
} else {
|
||||
outer -= quadCount;
|
||||
path.moveTo(cubics[outer][0].fX, cubics[outer][0].fY);
|
||||
path.cubicTo(cubics[outer][1].fX, cubics[outer][1].fY,
|
||||
cubics[outer][2].fX, cubics[outer][2].fY,
|
||||
cubics[outer][3].fX, cubics[outer][3].fY);
|
||||
c1Type = SkPath::kCubic_Verb;
|
||||
pts1 = cubics[outer];
|
||||
}
|
||||
}
|
||||
return c1Type;
|
||||
}
|
||||
|
||||
static void testPath(const SkPath& path, const SkPoint* pts1, SkPath::Verb c1Type,
|
||||
const SkPoint* pts2, SkPath::Verb c2Type) {
|
||||
SkTArray<SimplifyAddIntersectingTsTest::Contour> contour;
|
||||
SimplifyAddIntersectingTsTest::EdgeBuilder builder(path, contour);
|
||||
if (contour.count() < 2) {
|
||||
return;
|
||||
}
|
||||
SimplifyAddIntersectingTsTest::Contour& c1 = contour[0];
|
||||
SimplifyAddIntersectingTsTest::Contour& c2 = contour[1];
|
||||
addIntersectingTs(&c1, &c2);
|
||||
bool c1Intersected = c1.fSegments[0].intersected();
|
||||
bool c2Intersected = c2.fSegments[0].intersected();
|
||||
#if DEBUG_DUMP
|
||||
SkDebugf("%s %s (%1.9g,%1.9g %1.9g,%1.9g) %s %s (%1.9g,%1.9g %1.9g,%1.9g)\n",
|
||||
__FUNCTION__, SimplifyAddIntersectingTsTest::kLVerbStr[c1Type],
|
||||
pts1[0].fX, pts1[0].fY,
|
||||
pts1[c1Type].fX, pts1[c1Type].fY,
|
||||
c1Intersected ? "intersects" : "does not intersect",
|
||||
SimplifyAddIntersectingTsTest::kLVerbStr[c2Type],
|
||||
pts2[0].fX, pts2[0].fY,
|
||||
pts2[c2Type].fX, pts2[c2Type].fY);
|
||||
if (c1Intersected) {
|
||||
c1.dump();
|
||||
c2.dump();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static const int firstO = 6;
|
||||
static const int firstI = 1;
|
||||
|
||||
void SimplifyAddIntersectingTs_Test() {
|
||||
const SkPoint* pts1, * pts2;
|
||||
if (firstO > 0 || firstI > 0) {
|
||||
SkPath path;
|
||||
SkPath::Verb c1Type = setPath(firstO, path, pts1);
|
||||
SkPath path2(path);
|
||||
SkPath::Verb c2Type = setPath(firstI, path2, pts2);
|
||||
testPath(path2, pts1, c1Type, pts2, c2Type);
|
||||
}
|
||||
for (int o = 0; o < testCount; ++o) {
|
||||
SkPath path;
|
||||
SkPath::Verb c1Type = setPath(o, path, pts1);
|
||||
for (int i = 0; i < testCount; ++i) {
|
||||
SkPath path2(path);
|
||||
SkPath::Verb c2Type = setPath(i, path2, pts2);
|
||||
testPath(path2, pts1, c1Type, pts2, c2Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,116 +1,133 @@
|
||||
<html>
|
||||
<head>
|
||||
<div style="height:0">
|
||||
<div id="test_1div">
|
||||
path.moveTo(213.673737, 413.292938);
|
||||
path.lineTo(225.200134, 343.616821);
|
||||
path.lineTo(236.726532, 273.940704);
|
||||
path.lineTo(219.386414, 231.373322);
|
||||
path.lineTo(213.673737, 413.292938);
|
||||
path.close();
|
||||
path.moveTo(43.485352, 308.984497);
|
||||
path.lineTo(122.610657, 305.950134);
|
||||
path.lineTo(201.735962, 302.915802);
|
||||
path.lineTo(280.861267, 299.881470);
|
||||
path.lineTo(43.485352, 308.984497);
|
||||
path.close();
|
||||
|
||||
<div id="testSimplifyQuadratic1">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(1, 0, 1, 1);
|
||||
path.close();
|
||||
path.moveTo(1, 0);
|
||||
path.quadTo(0, 0, 0, 1);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
}
|
||||
</div>
|
||||
<div id="test_2div">
|
||||
path.moveTo(-177.878387, 265.368988);
|
||||
path.lineTo(-254.415771, 303.709961);
|
||||
path.lineTo(-317.465363, 271.325562);
|
||||
path.lineTo(-374.520386, 207.507660);
|
||||
path.lineTo(-177.878387, 265.368988);
|
||||
path.close();
|
||||
path.moveTo(-63.582489, -3.679123);
|
||||
path.lineTo(-134.496841, 26.434566);
|
||||
path.lineTo(-205.411209, 56.548256);
|
||||
path.lineTo(-276.325562, 86.661942);
|
||||
path.lineTo(-63.582489, -3.679123);
|
||||
path.close();
|
||||
path.moveTo(-57.078423, 162.633453);
|
||||
path.lineTo(-95.963928, 106.261139);
|
||||
path.lineTo(-134.849457, 49.888824);
|
||||
path.lineTo(-173.734955, -6.483480);
|
||||
path.lineTo(-57.078423, 162.633453);
|
||||
path.close();
|
||||
|
||||
<div id="testSimplifyQuadratic2">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(20, 0, 20, 20);
|
||||
path.close();
|
||||
path.moveTo(20, 0);
|
||||
path.quadTo(0, 0, 0, 20);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
}
|
||||
</div>
|
||||
<div id="test_3div">
|
||||
path.moveTo(98.666489, -94.295059);
|
||||
path.lineTo(156.584320, -61.939133);
|
||||
path.lineTo(174.672974, -12.343765);
|
||||
path.lineTo(158.622345, 52.028267);
|
||||
path.lineTo(98.666489, -94.295059);
|
||||
path.close();
|
||||
path.moveTo(-133.225616, -48.622055);
|
||||
path.lineTo(-73.855499, -10.375397);
|
||||
path.lineTo(-14.485367, 27.871277);
|
||||
path.lineTo(44.884750, 66.117935);
|
||||
path.lineTo(-133.225616, -48.622055);
|
||||
path.close();
|
||||
path.moveTo( 9.030045, -163.413132);
|
||||
path.lineTo(-19.605331, -89.588760);
|
||||
path.lineTo(-48.240707, -15.764404);
|
||||
path.lineTo(-76.876053, 58.059944);
|
||||
path.lineTo( 9.030045, -163.413132);
|
||||
path.close();
|
||||
|
||||
<div id="testSimplifyQuadratic3">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(20, 0, 20, 20);
|
||||
path.close();
|
||||
path.moveTo(0, 20);
|
||||
path.quadTo(0, 0, 20, 0);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
}
|
||||
</div>
|
||||
<div id="test_4div">
|
||||
path.moveTo(340.41568, -170.97171);
|
||||
path.lineTo(418.846893, -142.428329);
|
||||
path.lineTo(497.278107, -113.884933);
|
||||
path.lineTo(449.18222, -45.6723022);
|
||||
path.lineTo(340.41568, -170.97171);
|
||||
path.close();
|
||||
path.moveTo(326.610535, 34.0393639);
|
||||
path.lineTo(371.334595, -14.9620667);
|
||||
path.lineTo(416.058624, -63.9634857);
|
||||
path.lineTo(460.782654, -112.96492);
|
||||
path.lineTo(326.610535, 34.0393639);
|
||||
path.close();
|
||||
|
||||
<div id="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);
|
||||
}
|
||||
</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 id="testSimplifyQuadratic5">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 1);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</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 id="testSimplifyQuadratic6">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(1, 0);
|
||||
path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.quadTo(1, 0, 0, 1);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
<div id="test_7div">
|
||||
|
||||
<div id="testSimplifyQuadratic7">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(0, 1);
|
||||
path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.quadTo(1, 0, 0, 2);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="testSimplifyQuadratic8">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.quadTo(1, 0, 0, 2);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="testSimplifyQuadratic9">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(1, 1);
|
||||
path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.quadTo(1, 0, 2, 2);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="testSimplifyQuadratic10">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(0, 0);
|
||||
@ -118,67 +135,44 @@ path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 1);
|
||||
path.quadTo(1, 1, 1, 2);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="test_8div">
|
||||
original:
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(3, 1);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(1, 0);
|
||||
path.lineTo(0, 1);
|
||||
path.quadTo(2, 1, 3, 3);
|
||||
path.lineTo(1, 0);
|
||||
path.close();
|
||||
simplified:
|
||||
path.moveTo(1, 0);
|
||||
path.lineTo(0, 1);
|
||||
path.quadTo(1.42857146, 1, 2.34693885, 2.02040815);
|
||||
path.lineTo(1.28571427, 0.428571433);
|
||||
path.lineTo(1, 0);
|
||||
path.close();
|
||||
path.moveTo(2.34693885, 2.02040815);
|
||||
path.quadTo(2.71428561, 2.42857146, 3, 3);
|
||||
path.lineTo(2.34693885, 2.02040815);
|
||||
path.close();
|
||||
</div>
|
||||
|
||||
<div id="test_9div">
|
||||
<div id="testSimplifyQuadratic11">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(0, 2);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(2, 1);
|
||||
path.quadTo(2, 2, 3, 3);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="testSimplifyQuadratic12">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 2);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(3, 0);
|
||||
path.quadTo(1, 1, 0, 2);
|
||||
path.lineTo(3, 0);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="test_10div">
|
||||
original:
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 2);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(3, 0);
|
||||
path.quadTo(1, 1, 0, 2);
|
||||
path.lineTo(3, 0);
|
||||
path.close();
|
||||
simplified:
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 2);
|
||||
path.quadTo(1, 1, 3, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
</div>
|
||||
|
||||
<div id="test_11div">
|
||||
original:
|
||||
<div id="testSimplifyQuadratic13">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 1, 0);
|
||||
path.lineTo(1, 1);
|
||||
@ -188,56 +182,69 @@ path.moveTo(0, 0);
|
||||
path.quadTo(3, 0, 1, 1);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
simplified:
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(1, 1);
|
||||
path.lineTo(1, 0);
|
||||
path.lineTo(0, 0);
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="test_12div">
|
||||
<div id="testSimplifyQuadratic14">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(1, 1);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.quadTo(0, 1, 2, 1);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="test_13div">
|
||||
original:
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 1, 3);
|
||||
path.lineTo(3, 3);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(0, 1);
|
||||
path.lineTo(1, 1);
|
||||
path.quadTo(0, 3, 3, 3);
|
||||
path.lineTo(0, 1);
|
||||
path.close();
|
||||
simplified:
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0.333333343, 1);
|
||||
path.lineTo(0, 1);
|
||||
path.lineTo(0.428571433, 1.28571427);
|
||||
path.lineTo(0.333333343, 1);
|
||||
path.lineTo(1, 1);
|
||||
path.lineTo(0, 0);
|
||||
path.close();
|
||||
path.moveTo(1, 1);
|
||||
path.quadTo(0.857142866, 1.28571427, 0.795918345, 1.53061223);
|
||||
path.lineTo(0.428571433, 1.28571427);
|
||||
path.lineTo(1, 3);
|
||||
path.lineTo(3, 3);
|
||||
path.lineTo(0.795918345, 1.53061223);
|
||||
path.quadTo(0.428571433, 3, 3, 3);
|
||||
path.lineTo(1, 1);
|
||||
path.close();
|
||||
<div id="testSimplifyQuadratic15">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 1, 3);
|
||||
path.lineTo(3, 3);
|
||||
path.close();
|
||||
path.moveTo(0, 1);
|
||||
path.lineTo(1, 1);
|
||||
path.quadTo(0, 3, 3, 3);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="testSimplifyQuadratic16">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(0, 1);
|
||||
path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, 0);
|
||||
path.quadTo(1, 0, 0, 1);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
<div id="testSimplifyQuadratic17">
|
||||
SkPath path, out;
|
||||
path.moveTo(0, 0);
|
||||
path.quadTo(0, 0, 0, 0);
|
||||
path.lineTo(2, 2);
|
||||
path.close();
|
||||
path.moveTo(0, 1);
|
||||
path.lineTo(0, 1);
|
||||
path.quadTo(2, 1, 3, 3);
|
||||
path.close();
|
||||
testSimplify(path, true, out, bitmap);
|
||||
drawAsciiPaths(path, out, true);
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -245,19 +252,23 @@ path.close();
|
||||
<script type="text/javascript">
|
||||
|
||||
var testDivs = [
|
||||
test_13div,
|
||||
test_12div,
|
||||
test_11div,
|
||||
test_10div,
|
||||
test_9div,
|
||||
test_8div,
|
||||
test_7div,
|
||||
test_6div,
|
||||
test_5div,
|
||||
test_4div,
|
||||
test_3div,
|
||||
test_2div,
|
||||
test_1div,
|
||||
testSimplifyQuadratic17,
|
||||
testSimplifyQuadratic16,
|
||||
testSimplifyQuadratic15,
|
||||
testSimplifyQuadratic14,
|
||||
testSimplifyQuadratic13,
|
||||
testSimplifyQuadratic12,
|
||||
testSimplifyQuadratic11,
|
||||
testSimplifyQuadratic10,
|
||||
testSimplifyQuadratic9,
|
||||
testSimplifyQuadratic8,
|
||||
testSimplifyQuadratic7,
|
||||
testSimplifyQuadratic6,
|
||||
testSimplifyQuadratic5,
|
||||
testSimplifyQuadratic4,
|
||||
testSimplifyQuadratic3,
|
||||
testSimplifyQuadratic2,
|
||||
testSimplifyQuadratic1,
|
||||
];
|
||||
|
||||
var scale, columns, rows, xStart, yStart;
|
||||
@ -291,8 +302,18 @@ function parse(test) {
|
||||
if (pts.length > 0)
|
||||
verbs.push(pts);
|
||||
}
|
||||
if (verbs.length > 0)
|
||||
if (verbs.length > 0) {
|
||||
var lastIndex = verbs.length - 1;
|
||||
var lastVerb = verbs[lastIndex];
|
||||
var lastLen = lastVerb.length;
|
||||
if (verbs[0][0] != lastVerb[lastLen - 2] && verbs[0][1] != lastVerb[lastLen - 1]) {
|
||||
var lastPts = [];
|
||||
lastPts.push(verbs[0][0]);
|
||||
lastPts.push(verbs[0][1]);
|
||||
verbs.push(lastPts);
|
||||
}
|
||||
contours.push(verbs);
|
||||
}
|
||||
}
|
||||
if (contours.length > 0)
|
||||
tests.push(contours);
|
||||
@ -469,23 +490,24 @@ function doKeyPress(evt) {
|
||||
case 'n':
|
||||
if (++testIndex >= tests.length)
|
||||
testIndex = 0;
|
||||
// insetScale = scale;
|
||||
mouseX = Infinity;
|
||||
drawTop();
|
||||
break;
|
||||
case 'P':
|
||||
case 'p':
|
||||
if (--testIndex < 0)
|
||||
testIndex = tests.length - 1;
|
||||
mouseX = Infinity;
|
||||
drawTop();
|
||||
break;
|
||||
case 'T':
|
||||
case 't':
|
||||
// drawTs(testIndex);
|
||||
break;
|
||||
case '-':
|
||||
// if (--insetScale < 1)
|
||||
// insetScale = 1;
|
||||
// else
|
||||
redraw();
|
||||
break;
|
||||
case '=':
|
||||
case '+':
|
||||
// ++insetScale;
|
||||
redraw();
|
||||
break;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user