git-svn-id: http://skia.googlecode.com/svn/trunk@7788 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
caryclark@google.com 2013-02-20 12:51:37 +00:00
Родитель 76bf70d38f
Коммит 5e0500fb5f
18 изменённых файлов: 421 добавлений и 53 удалений

Просмотреть файл

@ -113,10 +113,10 @@ bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]) {
int lower0Index = 1; int lower0Index = 1;
int upper0Index = 1; int upper0Index = 1;
for (index = 2; index < 4; ++index) { for (index = 2; index < 4; ++index) {
if (approximately_greater(projectedY[lower0Index], projectedY[index])) { if (approximately_greater_or_equal(projectedY[lower0Index], projectedY[index])) {
lower0Index = index; lower0Index = index;
} }
if (approximately_lesser(projectedY[upper0Index], projectedY[index])) { if (approximately_lesser_or_equal(projectedY[upper0Index], projectedY[index])) {
upper0Index = index; upper0Index = index;
} }
} }
@ -129,10 +129,10 @@ bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]) {
int lower3Index = 2; int lower3Index = 2;
int upper3Index = 2; int upper3Index = 2;
for (index = 1; index > -1; --index) { for (index = 1; index > -1; --index) {
if (approximately_greater(projectedY[lower3Index], projectedY[index])) { if (approximately_greater_or_equal(projectedY[lower3Index], projectedY[index])) {
lower3Index = index; lower3Index = index;
} }
if (approximately_lesser(projectedY[upper3Index], projectedY[index])) { if (approximately_lesser_or_equal(projectedY[upper3Index], projectedY[index])) {
upper3Index = index; upper3Index = index;
} }
} }

Просмотреть файл

@ -54,17 +54,17 @@ bool bezier_clip(const Cubic& cubic1, const Cubic& cubic2, double& minT, double&
endLine.cubicDistanceY(cubic2, distance2y); endLine.cubicDistanceY(cubic2, distance2y);
int flags = 0; int flags = 0;
if (approximately_lesser(distance2y[0].y, top)) { if (approximately_lesser_or_equal(distance2y[0].y, top)) {
flags |= kFindTopMin; flags |= kFindTopMin;
} else if (approximately_greater(distance2y[0].y, bottom)) { } else if (approximately_greater_or_equal(distance2y[0].y, bottom)) {
flags |= kFindBottomMin; flags |= kFindBottomMin;
} else { } else {
minT = 0; minT = 0;
} }
if (approximately_lesser(distance2y[3].y, top)) { if (approximately_lesser_or_equal(distance2y[3].y, top)) {
flags |= kFindTopMax; flags |= kFindTopMax;
} else if (approximately_greater(distance2y[3].y, bottom)) { } else if (approximately_greater_or_equal(distance2y[3].y, bottom)) {
flags |= kFindBottomMax; flags |= kFindBottomMax;
} else { } else {
maxT = 1; maxT = 1;

Просмотреть файл

@ -14,7 +14,7 @@
#include "QuadraticUtilities.h" #include "QuadraticUtilities.h"
#if ONE_OFF_DEBUG #if ONE_OFF_DEBUG
static const double tLimits[2][2] = {{0.516980827, 0.516981209}, {0.647714088, 0.64771447}}; static const double tLimits[2][2] = {{0.599274754, 0.599275135}, {0.599274754, 0.599275135}};
#endif #endif
#define DEBUG_QUAD_PART 0 #define DEBUG_QUAD_PART 0
@ -358,6 +358,19 @@ static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic&
const double t2 = t2s + (t2e - t2s) * tEnd2; const double t2 = t2s + (t2e - t2s) * tEnd2;
Quadratic s2; Quadratic s2;
int o2 = quadPart(cubic2, t2Start, t2, s2); int o2 = quadPart(cubic2, t2Start, t2, s2);
#if ONE_OFF_DEBUG
if (tLimits[0][0] >= t1Start && tLimits[0][1] <= t1
&& tLimits[1][0] >= t2Start && tLimits[1][1] <= t2) {
Cubic cSub1, cSub2;
sub_divide(cubic1, t1Start, tEnd1, cSub1);
sub_divide(cubic2, t2Start, tEnd2, cSub2);
SkDebugf("t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)\n",
t1Start, t1, t2Start, t2);
Intersections xlocals;
intersectWithOrder(s1, o1, s2, o2, xlocals);
SkDebugf("xlocals.fUsed=%d\n", xlocals.used());
}
#endif
Intersections locals; Intersections locals;
intersectWithOrder(s1, o1, s2, o2, locals); intersectWithOrder(s1, o1, s2, o2, locals);
double coStart[2] = { -1 }; double coStart[2] = { -1 };

Просмотреть файл

@ -134,6 +134,12 @@ static const Cubic testSet[] = {
const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]); const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
static const Cubic newTestSet[] = { static const Cubic newTestSet[] = {
{{1,2},{5,6},{1,0},{1,0}},
{{0,1},{0,1},{2,1},{6,5}},
{{0,6},{1,2},{1,0},{1,0}},
{{0,1},{0,1},{6,0},{2,1}},
{{0,2},{0,1},{3,0},{1,0}}, {{0,2},{0,1},{3,0},{1,0}},
{{0,3},{0,1},{2,0},{1,0}}, {{0,3},{0,1},{2,0},{1,0}},
}; };
@ -544,10 +550,10 @@ void CubicIntersection_IntersectionFinder() {
const Cubic& cubic1 = newTestSet[0]; const Cubic& cubic1 = newTestSet[0];
const Cubic& cubic2 = newTestSet[1]; const Cubic& cubic2 = newTestSet[1];
double t1Seed = 0.99; double t1Seed = 0.599;
double t2Seed = 0.99; double t2Seed = 0.599;
double t1Step = 0.01; double t1Step = 0.1;
double t2Step = 0.01; double t2Step = 0.1;
_Point t1[3], t2[3]; _Point t1[3], t2[3];
bool toggle = true; bool toggle = true;
do { do {

Просмотреть файл

@ -112,6 +112,10 @@ int cubicRootsReal(double A, double B, double C, double D, double s[3]) {
char str[1024]; char str[1024];
bzero(str, sizeof(str)); bzero(str, sizeof(str));
sprintf(str, "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", A, B, C, D); sprintf(str, "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", A, B, C, D);
mathematica_ize(str, sizeof(str));
#if ONE_OFF_DEBUG
SkDebugf("%s\n", str);
#endif
#endif #endif
if (approximately_zero(A) if (approximately_zero(A)
&& approximately_zero_when_compared_to(A, B) && approximately_zero_when_compared_to(A, B)

Просмотреть файл

@ -65,3 +65,22 @@ int UlpsDiff(float A, float B)
return abs(uA.i - uB.i); return abs(uA.i - uB.i);
} }
#endif #endif
#if SK_DEBUG
void mathematica_ize(char* str, size_t bufferLen) {
size_t len = strlen(str);
bool num = false;
for (size_t idx = 0; idx < len; ++idx) {
if (num && str[idx] == 'e') {
if (len + 2 >= bufferLen) {
return;
}
memmove(&str[idx + 2], &str[idx + 1], len - idx);
str[idx] = '*';
str[idx + 1] = '^';
++len;
}
num = str[idx] >= '0' && str[idx] <= '9';
}
}
#endif

Просмотреть файл

@ -120,11 +120,19 @@ inline bool approximately_equal_squared(double x, double y) {
} }
inline bool approximately_greater(double x, double y) { inline bool approximately_greater(double x, double y) {
return approximately_equal(x, y) ? false : x > y; return x - FLT_EPSILON >= y;
}
inline bool approximately_greater_or_equal(double x, double y) {
return x + FLT_EPSILON > y;
} }
inline bool approximately_lesser(double x, double y) { inline bool approximately_lesser(double x, double y) {
return approximately_equal(x, y) ? false : x < y; return x + FLT_EPSILON <= y;
}
inline bool approximately_lesser_or_equal(double x, double y) {
return x - FLT_EPSILON < y;
} }
inline double approximately_pin(double x) { inline double approximately_pin(double x) {
@ -285,7 +293,10 @@ struct _Point {
double lengthSquared() const { double lengthSquared() const {
return x * x + y * y; return x * x + y * y;
} }
double roughlyEqual(const _Point& a) const {
return roughly_equal(a.y, y) && roughly_equal(a.x, x);
}
}; };
typedef _Point _Line[2]; typedef _Point _Line[2];
@ -361,4 +372,8 @@ struct QuadraticPair {
#define sk_double_isnan(a) sk_float_isnan(a) #define sk_double_isnan(a) sk_float_isnan(a)
#if SK_DEBUG
void mathematica_ize(char* str, size_t bufferSize);
#endif
#endif // __DataTypes_h__ #endif // __DataTypes_h__

Просмотреть файл

@ -15,15 +15,15 @@ void parseSVG();
void Intersection_Tests() { void Intersection_Tests() {
int testsRun = 0; int testsRun = 0;
#if 0 #if 0
#endif
QuadraticIntersection_IntersectionFinder(); QuadraticIntersection_IntersectionFinder();
QuadraticIntersection_OneOffTest(); QuadraticIntersection_OneOffTest();
CubicIntersection_IntersectionFinder(); CubicIntersection_IntersectionFinder();
CubicIntersection_NewOneOffTest(); CubicIntersection_NewOneOffTest();
#endif
SimplifyNew_Test(); SimplifyNew_Test();
CubicsToQuadratics_OneOffTest(); CubicsToQuadratics_OneOffTest();
CubicIntersection_OneOffTest(); CubicIntersection_OneOffTest();
CubicIntersection_OneOffTests(); // CubicIntersection_OneOffTests();
#if 0 #if 0
parseSVG(); parseSVG();
#endif #endif

Просмотреть файл

@ -41,17 +41,17 @@ bool bezier_clip(const Quadratic& q1, const Quadratic& q2, double& minT, double&
endLine.quadDistanceY(q2, distance2y); endLine.quadDistanceY(q2, distance2y);
int flags = 0; int flags = 0;
if (approximately_lesser(distance2y[0].y, top)) { if (approximately_lesser_or_equal(distance2y[0].y, top)) {
flags |= kFindTopMin; flags |= kFindTopMin;
} else if (approximately_greater(distance2y[0].y, bottom)) { } else if (approximately_greater_or_equal(distance2y[0].y, bottom)) {
flags |= kFindBottomMin; flags |= kFindBottomMin;
} else { } else {
minT = 0; minT = 0;
} }
if (approximately_lesser(distance2y[2].y, top)) { if (approximately_lesser_or_equal(distance2y[2].y, top)) {
flags |= kFindTopMax; flags |= kFindTopMax;
} else if (approximately_greater(distance2y[2].y, bottom)) { } else if (approximately_greater_or_equal(distance2y[2].y, bottom)) {
flags |= kFindBottomMax; flags |= kFindBottomMax;
} else { } else {
maxT = 1; maxT = 1;

Просмотреть файл

@ -25,7 +25,7 @@
*/ */
static int findRoots(const QuadImplicitForm& i, const Quadratic& q2, double roots[4], static int findRoots(const QuadImplicitForm& i, const Quadratic& q2, double roots[4],
bool oneHint) { bool oneHint, int firstCubicRoot) {
double a, b, c; double a, b, c;
set_abc(&q2[0].x, a, b, c); set_abc(&q2[0].x, a, b, c);
double d, e, f; double d, e, f;
@ -56,7 +56,7 @@ static int findRoots(const QuadImplicitForm& i, const Quadratic& q2, double root
if (rootCount >= 0) { if (rootCount >= 0) {
return rootCount; return rootCount;
} }
return quarticRootsReal(t4, t3, t2, t1, t0, roots); return quarticRootsReal(firstCubicRoot, t4, t3, t2, t1, t0, roots);
} }
static int addValidRoots(const double roots[4], const int count, double valid[4]) { static int addValidRoots(const double roots[4], const int count, double valid[4]) {
@ -334,6 +334,87 @@ static void unsortableExpanse(const Quadratic& q1, const Quadratic& q2, Intersec
} }
#endif #endif
// each time through the loop, this computes values it had from the last loop
// if i == j == 1, the center values are still good
// otherwise, for i != 1 or j != 1, four of the values are still good
// and if i == 1 ^ j == 1, an additional value is good
static bool binarySearch(const Quadratic& quad1, const Quadratic& quad2, double& t1Seed,
double& t2Seed, _Point& pt) {
double tStep = ROUGH_EPSILON;
_Point t1[3], t2[3];
int calcMask = ~0;
do {
if (calcMask & (1 << 1)) t1[1] = xy_at_t(quad1, t1Seed);
if (calcMask & (1 << 4)) t2[1] = xy_at_t(quad2, t2Seed);
if (t1[1].approximatelyEqual(t2[1])) {
pt = t1[1];
#if ONE_OFF_DEBUG
SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) == (%1.9g,%1.9g)\n", __FUNCTION__,
t1Seed, t2Seed, t1[1].x, t1[1].y, t1[2].x, t1[2].y);
#endif
return true;
}
if (calcMask & (1 << 0)) t1[0] = xy_at_t(quad1, t1Seed - tStep);
if (calcMask & (1 << 2)) t1[2] = xy_at_t(quad1, t1Seed + tStep);
if (calcMask & (1 << 3)) t2[0] = xy_at_t(quad2, t2Seed - tStep);
if (calcMask & (1 << 5)) t2[2] = xy_at_t(quad2, t2Seed + tStep);
double dist[3][3];
// OPTIMIZE: using calcMask value permits skipping some distance calcuations
// if prior loop's results are moved to correct slot for reuse
dist[1][1] = t1[1].distanceSquared(t2[1]);
int best_i = 1, best_j = 1;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (i == 1 && j == 1) {
continue;
}
dist[i][j] = t1[i].distanceSquared(t2[j]);
if (dist[best_i][best_j] > dist[i][j]) {
best_i = i;
best_j = j;
}
}
}
if (best_i == 1 && best_j == 1) {
tStep /= 2;
if (tStep < FLT_EPSILON_HALF) {
break;
}
calcMask = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 5);
continue;
}
if (best_i == 0) {
t1Seed -= tStep;
t1[2] = t1[1];
t1[1] = t1[0];
calcMask = 1 << 0;
} else if (best_i == 2) {
t1Seed += tStep;
t1[0] = t1[1];
t1[1] = t1[2];
calcMask = 1 << 2;
} else {
calcMask = 0;
}
if (best_j == 0) {
t2Seed -= tStep;
t2[2] = t2[1];
t2[1] = t2[0];
calcMask |= 1 << 3;
} else if (best_j == 2) {
t2Seed += tStep;
t2[0] = t2[1];
t2[1] = t2[2];
calcMask |= 1 << 5;
}
} while (true);
#if ONE_OFF_DEBUG
SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) != (%1.9g,%1.9g) %s\n", __FUNCTION__,
t1Seed, t2Seed, t1[1].x, t1[1].y, t1[2].x, t1[2].y);
#endif
return false;
}
bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) { bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
// if the quads share an end point, check to see if they overlap // if the quads share an end point, check to see if they overlap
@ -378,7 +459,7 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
int index; int index;
bool useCubic = q1[0] == q2[0] || q1[0] == q2[2] || q1[2] == q2[0]; bool useCubic = q1[0] == q2[0] || q1[0] == q2[2] || q1[2] == q2[0];
double roots1[4]; double roots1[4];
int rootCount = findRoots(i2, q1, roots1, useCubic); int rootCount = findRoots(i2, q1, roots1, useCubic, 0);
// OPTIMIZATION: could short circuit here if all roots are < 0 or > 1 // OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
double roots1Copy[4]; double roots1Copy[4];
int r1Count = addValidRoots(roots1, rootCount, roots1Copy); int r1Count = addValidRoots(roots1, rootCount, roots1Copy);
@ -387,7 +468,7 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
xy_at_t(q1, roots1Copy[index], pts1[index].x, pts1[index].y); xy_at_t(q1, roots1Copy[index], pts1[index].x, pts1[index].y);
} }
double roots2[4]; double roots2[4];
int rootCount2 = findRoots(i1, q2, roots2, useCubic); int rootCount2 = findRoots(i1, q2, roots2, useCubic, 0);
double roots2Copy[4]; double roots2Copy[4];
int r2Count = addValidRoots(roots2, rootCount2, roots2Copy); int r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
_Point pts2[4]; _Point pts2[4];
@ -398,6 +479,39 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
if (r1Count == 1) { if (r1Count == 1) {
if (pts1[0].approximatelyEqualHalf(pts2[0])) { if (pts1[0].approximatelyEqualHalf(pts2[0])) {
i.insert(roots1Copy[0], roots2Copy[0], pts1[0]); i.insert(roots1Copy[0], roots2Copy[0], pts1[0]);
} else if (pts1[0].roughlyEqual(pts2[0])) {
// experiment: see if a different cubic solution provides the correct quartic answer
#if 0
for (int cu1 = 0; cu1 < 3; ++cu1) {
rootCount = findRoots(i2, q1, roots1, useCubic, cu1);
r1Count = addValidRoots(roots1, rootCount, roots1Copy);
if (r1Count == 0) {
continue;
}
for (int cu2 = 0; cu2 < 3; ++cu2) {
if (cu1 == 0 && cu2 == 0) {
continue;
}
rootCount2 = findRoots(i1, q2, roots2, useCubic, cu2);
r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
if (r2Count == 0) {
continue;
}
SkASSERT(r1Count == 1 && r2Count == 1);
SkDebugf("*** [%d,%d] (%1.9g,%1.9g) %s (%1.9g,%1.9g)\n", cu1, cu2,
pts1[0].x, pts1[0].y, pts1[0].approximatelyEqualHalf(pts2[0])
? "==" : "!=", pts2[0].x, pts2[0].y);
}
}
#endif
// experiment: try to find intersection by chasing t
rootCount = findRoots(i2, q1, roots1, useCubic, 0);
r1Count = addValidRoots(roots1, rootCount, roots1Copy);
rootCount2 = findRoots(i1, q2, roots2, useCubic, 0);
r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
if (binarySearch(q1, q2, roots1Copy[0], roots2Copy[0], pts1[0])) {
i.insert(roots1Copy[0], roots2Copy[0], pts1[0]);
}
} }
} }
return i.intersected(); return i.intersected();

Просмотреть файл

@ -54,6 +54,15 @@ static void standardTestCases() {
} }
static const Quadratic testSet[] = { static const Quadratic testSet[] = {
{{2.7279999999999998, 3.024}, {2.5600000000000005, 2.5600000000000005}, {2.1520000000000001, 1.8560000000000001}},
{{0.66666666666666652, 1.1481481481481481}, {1.3333333333333326, 1.3333333333333335}, {2.6666666666666665, 2.1851851851851851}},
{{2.728,3.024}, {2.56,2.56}, {2.152,1.856}},
{{0.666666667,1.14814815}, {1.33333333,1.33333333}, {2.66666667,2.18518519}},
{{0.875,1.5}, {1.03125,1.11022302e-16}, {1,0}},
{{0.875,0.859375}, {1.6875,0.73046875}, {2.5,0.625}},
{{1.64451042,0.0942001592}, {1.53635465,0.00152863961}, {1,0}}, {{1.64451042,0.0942001592}, {1.53635465,0.00152863961}, {1,0}},
{{1.27672209,0.15}, {1.32143477,9.25185854e-17}, {1,0}}, {{1.27672209,0.15}, {1.32143477,9.25185854e-17}, {1,0}},
@ -196,6 +205,7 @@ static void oneOffTest1(size_t outer, size_t inner) {
void QuadraticIntersection_OneOffTest() { void QuadraticIntersection_OneOffTest() {
oneOffTest1(0, 1); oneOffTest1(0, 1);
oneOffTest1(2, 3);
} }
static void oneOffTests() { static void oneOffTests() {
@ -297,14 +307,14 @@ void QuadraticIntersection_PointFinder() {
hullIntersect(pointFinderTestSet[0], pointFinderTestSet[6]); hullIntersect(pointFinderTestSet[0], pointFinderTestSet[6]);
} }
void QuadraticIntersection_IntersectionFinder() { static void intersectionFinder(int test1, int test2) {
const Quadratic& quad1 = testSet[0]; const Quadratic& quad1 = testSet[test1];
const Quadratic& quad2 = testSet[1]; const Quadratic& quad2 = testSet[test2];
double t1Seed = 0.98; double t1Seed = 0.989;
double t2Seed = 0.97; double t2Seed = 0.800;
double t1Step = 0.05; double t1Step = 0.01;
double t2Step = 0.05; double t2Step = 0.01;
_Point t1[3], t2[3]; _Point t1[3], t2[3];
bool toggle = true; bool toggle = true;
do { do {
@ -388,3 +398,8 @@ void QuadraticIntersection_IntersectionFinder() {
SkDebugf("%s p2=(%1.9g,%1.9g)<(%1.9g,%1.9g)<(%1.9g,%1.9g)\n", __FUNCTION__, SkDebugf("%s p2=(%1.9g,%1.9g)<(%1.9g,%1.9g)<(%1.9g,%1.9g)\n", __FUNCTION__,
p20.x, p20.y, p2Seed.x, p2Seed.y, p22.x, p22.y); p20.x, p20.y, p2Seed.x, p2Seed.y, p22.x, p22.y);
} }
void QuadraticIntersection_IntersectionFinder() {
intersectionFinder(0, 1);
intersectionFinder(2, 3);
}

Просмотреть файл

@ -40,17 +40,22 @@ int reducedQuarticRoots(const double t4, const double t3, const double t2, const
bzero(str, sizeof(str)); bzero(str, sizeof(str));
sprintf(str, "Solve[%1.19g x^4 + %1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", sprintf(str, "Solve[%1.19g x^4 + %1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
t4, t3, t2, t1, t0); t4, t3, t2, t1, t0);
mathematica_ize(str, sizeof(str));
#if ONE_OFF_DEBUG
SkDebugf("%s\n", str);
#endif
#endif #endif
if (approximately_zero_when_compared_to(t4, t0) // 0 is one root if (approximately_zero_when_compared_to(t4, t0) // 0 is one root
&& approximately_zero_when_compared_to(t4, t1) && approximately_zero_when_compared_to(t4, t1)
&& approximately_zero_when_compared_to(t4, t2) && approximately_zero_when_compared_to(t4, t2)) {
&& approximately_zero_when_compared_to(t4, t3)) {
if (approximately_zero_when_compared_to(t3, t0) if (approximately_zero_when_compared_to(t3, t0)
&& approximately_zero_when_compared_to(t3, t1) && approximately_zero_when_compared_to(t3, t1)
&& approximately_zero_when_compared_to(t3, t2)) { && approximately_zero_when_compared_to(t3, t2)) {
return quadraticRootsReal(t2, t1, t0, roots); return quadraticRootsReal(t2, t1, t0, roots);
} }
return cubicRootsReal(t3, t2, t1, t0, roots); if (approximately_zero_when_compared_to(t4, t3)) {
return cubicRootsReal(t3, t2, t1, t0, roots);
}
} }
if (approximately_zero_when_compared_to(t0, t1) // 0 is one root if (approximately_zero_when_compared_to(t0, t1) // 0 is one root
&& approximately_zero_when_compared_to(t0, t2) && approximately_zero_when_compared_to(t0, t2)
@ -79,8 +84,8 @@ int reducedQuarticRoots(const double t4, const double t3, const double t2, const
return -1; return -1;
} }
int quarticRootsReal(const double A, const double B, const double C, const double D, int quarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
const double E, double s[4]) { const double D, const double E, double s[4]) {
double u, v; double u, v;
/* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */ /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
const double invA = 1 / A; const double invA = 1 / A;
@ -149,7 +154,7 @@ int quarticRootsReal(const double A, const double B, const double C, const doubl
double z; double z;
num = 0; num = 0;
int num2 = 0; int num2 = 0;
for (index = 0; index < roots; ++index) { for (index = firstCubicRoot; index < roots; ++index) {
z = cubicRoots[index]; z = cubicRoots[index];
/* ... to build two quadric equations */ /* ... to build two quadric equations */
u = z * z - r; u = z * z - r;

Просмотреть файл

@ -1,5 +1,5 @@
int reducedQuarticRoots(const double t4, const double t3, const double t2, const double t1, int reducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
const double t0, const bool oneHint, double s[4]); const double t0, const bool oneHint, double s[4]);
int quarticRootsReal(const double A, const double B, const double C, const double D, int quarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
const double E, double s[4]); const double D, const double E, double s[4]);

Просмотреть файл

@ -131,7 +131,7 @@ static void testOneQuartic(size_t aIndex, size_t bIndex, size_t cIndex, size_t d
bool oneHint = approximately_zero(A + b + c + d + e); bool oneHint = approximately_zero(A + b + c + d + e);
int rootCount = reducedQuarticRoots(A, b, c, d, e, oneHint, roots); int rootCount = reducedQuarticRoots(A, b, c, d, e, oneHint, roots);
if (rootCount < 0) { if (rootCount < 0) {
rootCount = quarticRootsReal(A, b, c, d, e, roots); rootCount = quarticRootsReal(0, A, b, c, d, e, roots);
} }
const int expected = 1 + (B != C) + (B != D && C != D) + (B != E && C != E && D != E); const int expected = 1 + (B != C) + (B != D && C != D) + (B != E && C != E && D != E);
SkASSERT(rootCount == expected); SkASSERT(rootCount == expected);

Просмотреть файл

@ -32,7 +32,7 @@ int gDebugMaxWindValue = SK_MaxS32;
#define DEBUG_UNUSED 0 // set to expose unused functions #define DEBUG_UNUSED 0 // set to expose unused functions
#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging #define FORCE_RELEASE 1 // set force release to 1 for multiple thread -- no debugging
#if FORCE_RELEASE || defined SK_RELEASE #if FORCE_RELEASE || defined SK_RELEASE
@ -51,8 +51,8 @@ const bool gRunTestsInOneThread = false;
#define DEBUG_MARK_DONE 0 #define DEBUG_MARK_DONE 0
#define DEBUG_PATH_CONSTRUCTION 0 #define DEBUG_PATH_CONSTRUCTION 0
#define DEBUG_SHOW_WINDING 0 #define DEBUG_SHOW_WINDING 0
#define DEBUG_SORT 0 #define DEBUG_SORT 1
#define DEBUG_SWAP_TOP 1 #define DEBUG_SWAP_TOP 0
#define DEBUG_UNSORTABLE 0 #define DEBUG_UNSORTABLE 0
#define DEBUG_WIND_BUMP 0 #define DEBUG_WIND_BUMP 0
#define DEBUG_WINDING 0 #define DEBUG_WINDING 0
@ -88,13 +88,16 @@ const bool gRunTestsInOneThread = true;
DEBUG_PATH_CONSTRUCTION) DEBUG_PATH_CONSTRUCTION)
#if DEBUG_DUMP #if DEBUG_DUMP
static const char* kShapeOpStr[] = {"diff", "sect", "union", "xor"};
static const char* kLVerbStr[] = {"", "line", "quad", "cubic"}; static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"}; // static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
static int gContourID; static int gContourID;
static int gSegmentID; static int gSegmentID;
#endif #endif
#if DEBUG_ACTIVE_OP
static const char* kShapeOpStr[] = {"diff", "sect", "union", "xor"};
#endif
#ifndef DEBUG_TEST #ifndef DEBUG_TEST
#define DEBUG_TEST 0 #define DEBUG_TEST 0
#endif #endif
@ -1041,7 +1044,7 @@ static bool useInnerWinding(int outerWinding, int innerWinding) {
int absIn = abs(innerWinding); int absIn = abs(innerWinding);
bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn; bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
if (outerWinding * innerWinding < 0) { if (outerWinding * innerWinding < 0) {
#if DEBUG_WINDING #if 0 && DEBUG_WINDING
SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__, SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
outerWinding, innerWinding, result ? "true" : "false"); outerWinding, innerWinding, result ? "true" : "false");
#endif #endif
@ -2822,14 +2825,24 @@ public:
endIndex = angle->start(); endIndex = angle->start();
} while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone); } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
if (leftSegment->verb() >= SkPath::kQuad_Verb) { if (leftSegment->verb() >= SkPath::kQuad_Verb) {
bool bumpsUp = leftSegment->bumpsUp(tIndex, endIndex);
SkPoint xyE = leftSegment->xyAtT(endIndex);
SkPoint xyS = leftSegment->xyAtT(tIndex);
SkPoint dxyE = leftSegment->dxdy(endIndex); SkPoint dxyE = leftSegment->dxdy(endIndex);
SkPoint dxyS = leftSegment->dxdy(tIndex); SkPoint dxyS = leftSegment->dxdy(tIndex);
double cross = dxyE.cross(dxyS); double cross = dxyE.cross(dxyS);
bool bumpCheck = bumpsUp && xyE.fY < xyS.fY;
#if DEBUG_SWAP_TOP #if DEBUG_SWAP_TOP
SkDebugf("%s dxyE=(%1.9g,%1.9g) dxyS=(%1.9g,%1.9g) cross=%1.9g\n", __FUNCTION__, SkDebugf("%s xyE=(%1.9g,%1.9g) xyS=(%1.9g,%1.9g)\n", __FUNCTION__,
dxyE.fX, dxyE.fY, dxyS.fX, dxyS.fY, cross); xyE.fX, xyE.fY, xyS.fX, xyS.fY);
SkDebugf("%s dxyE=(%1.9g,%1.9g) dxyS=(%1.9g,%1.9g) cross=%1.9g bump=%s\n", __FUNCTION__,
dxyE.fX, dxyE.fY, dxyS.fX, dxyS.fY, cross, bumpCheck ? "true" : "false");
#endif #endif
if (cross >= 1) { if ((cross > 0) ^ bumpCheck) {
leftSegment->bumpsUp(tIndex, endIndex);
SkDebugf("%s cross bump disagree\n", __FUNCTION__);
}
if (bumpCheck) {
#if DEBUG_SWAP_TOP #if DEBUG_SWAP_TOP
SkDebugf("%s swap\n", __FUNCTION__); SkDebugf("%s swap\n", __FUNCTION__);
#endif #endif
@ -3298,6 +3311,27 @@ the same winding is shared by both.
return &span; return &span;
} }
bool bumpsUp(int tStart, int tEnd) const {
SkPoint edge[4];
(*SegmentSubDivide[fVerb])(fPts, fTs[tStart].fT, fTs[tEnd].fT, edge);
switch (fVerb) {
case SkPath::kLine_Verb:
SkASSERT(0); // shouldn't call in for lines
return true;
case SkPath::kQuad_Verb:
return approximately_greater(edge[0].fY, edge[1].fY)
&& approximately_lesser(edge[1].fY, edge[2].fY);
case SkPath::kCubic_Verb:
return (approximately_greater(edge[0].fY, edge[1].fY)
&& approximately_lesser(edge[1].fY, edge[3].fY))
|| (approximately_greater(edge[0].fY, edge[2].fY)
&& approximately_lesser(edge[2].fY, edge[3].fY));
default:
SkASSERT(0);
return false;
}
}
Span* verifyOneWinding(const char* funName, int tIndex) { Span* verifyOneWinding(const char* funName, int tIndex) {
Span& span = fTs[tIndex]; Span& span = fTs[tIndex];
if (span.fDone) { if (span.fDone) {
@ -3685,7 +3719,8 @@ the same winding is shared by both.
int lesser = SkMin32(index, endIndex); int lesser = SkMin32(index, endIndex);
int oppWinding = oppSum(lesser); int oppWinding = oppSum(lesser);
int oppSpanWinding = oppSign(index, endIndex); int oppSpanWinding = oppSign(index, endIndex);
if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) { if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)
&& oppWinding != SK_MaxS32) {
oppWinding -= oppSpanWinding; oppWinding -= oppSpanWinding;
} }
return oppWinding; return oppWinding;
@ -3707,7 +3742,7 @@ the same winding is shared by both.
int lesser = SkMin32(index, endIndex); int lesser = SkMin32(index, endIndex);
int winding = windSum(lesser); int winding = windSum(lesser);
int spanWinding = spanSign(index, endIndex); int spanWinding = spanSign(index, endIndex);
if (winding && useInnerWinding(winding - spanWinding, winding)) { if (winding && useInnerWinding(winding - spanWinding, winding) && winding != SK_MaxS32) {
winding -= spanWinding; winding -= spanWinding;
} }
return winding; return winding;
@ -4130,6 +4165,8 @@ the same winding is shared by both.
start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end, start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(), segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
mSpan.fWindValue); mSpan.fWindValue);
start here;
// create an inline to replace this conditional
if (mSpan.fWindSum == SK_MinS32) { if (mSpan.fWindSum == SK_MinS32) {
SkDebugf("?"); SkDebugf("?");
} else { } else {

Просмотреть файл

@ -3845,12 +3845,68 @@ static void cubicOp20d() {
testShapeOp(path, pathB, kDifference_Op); testShapeOp(path, pathB, kDifference_Op);
} }
static void (*firstTest)() = cubicOp20d; static void cubicOp21d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(0,1, 2,1, 6,5);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,2);
pathB.cubicTo(5,6, 1,0, 1,0);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp22d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,3, 3,0, 2,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,3);
pathB.cubicTo(1,2, 1,0, 3,2);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp23d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,2, 4,0, 2,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(1,2, 1,0, 2,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp24d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,2, 2,0, 3,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,2);
pathB.cubicTo(2,3, 1,0, 2,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void (*firstTest)() = 0;
static struct { static struct {
void (*fun)(); void (*fun)();
const char* str; const char* str;
} tests[] = { } tests[] = {
TEST(cubicOp24d),
TEST(cubicOp23d),
TEST(cubicOp22d),
TEST(cubicOp21d),
TEST(cubicOp20d), TEST(cubicOp20d),
TEST(cubicOp19i), TEST(cubicOp19i),
TEST(cubicOp18d), TEST(cubicOp18d),

Просмотреть файл

@ -3600,11 +3600,59 @@ path.addRect(4, 13, 13, 16, SkPath::kCCW_Direction);
pathB.close(); pathB.close();
</div> </div>
<div id="cubicOp21d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(0,1, 2,1, 6,5);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,2);
pathB.cubicTo(5,6, 1,0, 1,0);
pathB.close();
</div>
<div id="cubicOp22d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,3, 3,0, 2,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,3);
pathB.cubicTo(1,2, 1,0, 3,2);
pathB.close();
</div>
<div id="cubicOp23d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,2, 4,0, 2,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(1,2, 1,0, 2,1);
pathB.close();
</div>
<div id="cubicOp24d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,2, 2,0, 3,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,2);
pathB.cubicTo(2,3, 1,0, 2,1);
pathB.close();
</div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
var testDivs = [ var testDivs = [
cubicOp24d,
cubicOp23d,
cubicOp22d,
cubicOp21d,
cubicOp20d, cubicOp20d,
cubicOp19i, cubicOp19i,
cubicOp18d, cubicOp18d,

Просмотреть файл

@ -1998,11 +1998,47 @@ quad=(1.20573276,0.813456177 1.2679289,0.830085346 1.33333333,0.851851852)
{{1.27672209,0.15}, {1.32143477,9.25185854e-17}, {1,0}}, {{1.27672209,0.15}, {1.32143477,9.25185854e-17}, {1,0}},
</div> </div>
<div id="cubicOp20d">
{{0,6},{1,2},{1,0},{1,0}},
{{0,1},{0,1},{6,0},{2,1}},
{{0,6}, {0.71875,3}, {0.875,1.5}},
{{0.875,1.5}, {1.03125,1.11022302e-16}, {1,0}},
{{0,1}, {0.0625,0.98828125}, {0.875,0.859375}},
{{0.875,0.859375}, {1.6875,0.73046875}, {2.5,0.625}},
{{2.5,0.625}, {3.3125,0.51953125}, {3.375,0.578125}},
{{3.375,0.578125}, {3.4375,0.63671875}, {2,1}},
</div>
<div id="cubicOp21d">
{{1,2},{5,6},{1,0},{1,0}},
{{0,1},{0,1},{2,1},{6,5}},
{{1,2}, {2.176,3.168}, {2.536,3.328}},
{{2.536,3.328}, {2.896,3.488}, {2.728,3.024}},
{{2.728,3.024}, {2.56,2.56}, {2.152,1.856}},
{{2.152,1.856}, {1.744,1.152}, {1.384,0.592}},
{{1.384,0.592}, {1.024,0.032}, {1,0}},
{{0,1}, {7.40148683e-17,0.962962963}, {0.666666667,1.14814815}},
{{0.666666667,1.14814815}, {1.33333333,1.33333333}, {2.66666667,2.18518519}},
{{2.66666667,2.18518519}, {4,3.03703704}, {6,5}},
</div>
<div id="quadOp21d">
{{2.728,3.024}, {2.56,2.56}, {2.152,1.856}},
{{0.666666667,1.14814815}, {1.33333333,1.33333333}, {2.66666667,2.18518519}},
</div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
var testDivs = [ var testDivs = [
quadOp21d,
cubicOp21d,
cubicOp20d,
quadOp16d, quadOp16d,
cubicOp16d, cubicOp16d,
cubicTest7, cubicTest7,