The original instantiation of pathops was in the experimental/Intersection directory. Anything of value has been copied into the mainline.

The obsolete gyp files are also included, along with a pixman test that never functioned but accidentally referenced some of these deleted files.

Review URL: https://codereview.chromium.org/867213004
This commit is contained in:
caryclark 2015-01-29 10:43:09 -08:00 committed by Commit bot
parent f75a130c45
commit 17a2b5473d
128 changed files with 0 additions and 59965 deletions

View File

@ -1,83 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Simplify.h"
namespace UnitTest {
#include "EdgeWalker.cpp"
} // end of UnitTest namespace
#include "Intersection_Tests.h"
SkPoint leftRight[][4] = {
// equal length
{{10, 10}, {10, 50}, {20, 10}, {20, 50}},
{{10, 10}, {10, 50}, {10, 10}, {20, 50}},
{{10, 10}, {10, 50}, {20, 10}, {10, 50}},
// left top higher
{{10, 0}, {10, 50}, {20, 10}, {20, 50}},
{{10, 0}, {10, 50}, {10, 10}, {20, 50}},
{{10, 0}, {10, 50}, {20, 10}, {10, 50}},
{{10, 0}, {10, 50}, {20, 10}, {10 + 0.000001f, 40}},
// left top lower
{{10, 20}, {10, 50}, {20, 10}, {20, 50}},
{{10, 20}, {10, 50}, {10, 10}, {20, 50}},
{{10, 20}, {10, 50}, {20, 10}, {10, 50}},
{{10, 20}, {10, 50}, {20, 10}, {10 + 0.000001f, 40}},
{{10, 20}, {10, 50}, { 0, 0}, {50, 50}},
// left bottom higher
{{10, 10}, {10, 40}, {20, 10}, {20, 50}},
{{10, 10}, {10, 40}, {10, 10}, {20, 50}},
{{10, 10}, {10, 40}, {20, 10}, {10, 50}},
{{10, 10}, {10, 40}, {20, 10}, { 0 + 0.000001f, 70}},
// left bottom lower
{{10, 10}, {10, 60}, {20, 10}, {20, 50}},
{{10, 10}, {10, 60}, {10, 10}, {20, 50}},
{{10, 10}, {10, 60}, {20, 10}, {10 + 0.000001f, 50}},
{{10, 10}, {10, 60}, {20, 10}, {10 + 0.000001f, 40}},
{{10, 10}, {10, 60}, { 0, 0}, {20 + 0.000001f, 20}},
};
size_t leftRightCount = sizeof(leftRight) / sizeof(leftRight[0]);
// older code that worked mostly
static bool operator_less_than(const UnitTest::ActiveEdge& lh,
const UnitTest::ActiveEdge& rh) {
if ((rh.fAbove.fY - lh.fAbove.fY > lh.fBelow.fY - rh.fAbove.fY
&& lh.fBelow.fY < rh.fBelow.fY)
|| (lh.fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - lh.fAbove.fY
&& rh.fBelow.fY < lh.fBelow.fY)) {
const SkPoint& check = rh.fBelow.fY <= lh.fBelow.fY
&& lh.fBelow != rh.fBelow ? rh.fBelow :
rh.fAbove;
return (check.fY - lh.fAbove.fY) * (lh.fBelow.fX - lh.fAbove.fX)
< (lh.fBelow.fY - lh.fAbove.fY) * (check.fX - lh.fAbove.fX);
}
const SkPoint& check = lh.fBelow.fY <= rh.fBelow.fY
&& lh.fBelow != rh.fBelow ? lh.fBelow : lh.fAbove;
return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
< (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
}
void ActiveEdge_Test() {
UnitTest::InEdge leftIn, rightIn;
UnitTest::ActiveEdge left, right;
left.fWorkEdge.fEdge = &leftIn;
right.fWorkEdge.fEdge = &rightIn;
for (size_t x = 0; x < leftRightCount; ++x) {
left.fAbove = leftRight[x][0];
left.fTangent = left.fBelow = leftRight[x][1];
right.fAbove = leftRight[x][2];
right.fTangent = right.fBelow = leftRight[x][3];
SkASSERT(left < right);
SkASSERT(operator_less_than(left, right));
SkASSERT(!(right < left));
SkASSERT(!operator_less_than(right, left));
}
}

View File

@ -1,115 +0,0 @@
#include "SkStream.h"
#include "SkString.h"
#include "SkTDArray.h"
#include <stdio.h>
static bool replace(const char* fun, const char* dir, const char* filename, const char* marker,
const char* marker2, const char* replace, size_t replaceLen) {
SkString outFileStr(dir);
outFileStr.append(filename);
SkFILEStream opStreamIn(outFileStr.c_str());
if (!opStreamIn.isValid()) {
SkDebugf("%s couldn't find %s\n", fun, outFileStr.c_str());
return false;
}
SkTDArray<char> opData;
opData.setCount(opStreamIn.getLength());
size_t opLen = opData.count();
opStreamIn.read(opData.begin(), opLen);
opStreamIn.setPath(NULL);
SkFILEWStream opStreamOut(outFileStr.c_str());
if (!opStreamOut.isValid()) {
SkDebugf("%s couldn't open for writing %s\n", fun, outFileStr.c_str());
return false;
}
char* opInsert = strstr(opData.begin(), marker);
if (!opInsert) {
SkDebugf("%s missing marker in %s\n", fun, outFileStr.c_str());
opStreamOut.write(opData.begin(), opLen);
opStreamOut.flush();
return false;
}
const char* opInsertEnd = opInsert + strlen(marker);
if (marker2) {
char* opInsert2 = strstr(opInsert, marker2);
if (!opInsert2) {
SkDebugf("%s missing marker second half in %s\n", fun, outFileStr.c_str());
opStreamOut.write(opData.begin(), opLen);
opStreamOut.flush();
return false;
}
opInsertEnd = opInsert2 + strlen(marker2);
}
opStreamOut.write(opData.begin(), opInsert - opData.begin());
opStreamOut.write(replace, replaceLen);
opStreamOut.write(opInsertEnd, opLen - (opInsertEnd - opData.begin()));
opStreamOut.flush();
return true;
}
int main (int argc, char * const argv[]) {
if (argc != 2) {
SkDebugf("%s expected filename\n", argv[0]);
return 0;
}
const char* dir = "../../experimental/Intersection/";
SkString inFileStr;
if (argv[1][0] != '/') {
inFileStr.append(dir);
}
inFileStr.append(argv[1]);
SkFILEStream inFile(inFileStr.c_str());
if (!inFile.isValid()) {
SkDebugf("%s couldn't find %s\n", argv[0], argv[1]);
return 0;
}
SkTDArray<char> inData;
inData.setCount(inFile.getLength());
size_t inLen = inData.count();
inFile.read(inData.begin(), inLen);
inFile.setPath(NULL);
char* insert = strstr(inData.begin(), "\n\n\n");
if (!insert) {
SkDebugf("%s missing two blank line delimiter in %s\n", argv[0], argv[1]);
return 0;
}
insert += 1; // include first blank line
const char opMarker[] =
"</div>" "\n"
"\n"
"<script type=\"text/javascript\">" "\n"
"\n"
"var testDivs = [" "\n"
;
if (!replace(argv[0], dir, "op.htm", opMarker, NULL, inData.begin(),
insert - inData.begin())) {
return 0;
}
const char newMarker[] =
"static void (*firstTest)() = "
;
const char newMarker2[] =
";" "\n"
"\n"
"static struct {" "\n"
" void (*fun)();" "\n"
" const char* str;" "\n"
"} tests[] = {" "\n"
;
if (!replace(argv[0], dir, "SimplifyNew_Test.cpp", newMarker, newMarker2, insert + 2,
inLen - (insert - inData.begin()) - 2)) {
return 0;
}
const char forceReleaseMarker[] =
"#define FORCE_RELEASE 1 // set force release to 1 for multiple thread -- no debugging"
;
const char forceReleaseReplace[] =
"#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging"
;
if (!replace(argv[0], dir, "DataTypes.h", forceReleaseMarker, NULL, forceReleaseReplace,
sizeof(forceReleaseReplace) - 1)) {
return 0;
}
return 0;
}

View File

@ -1,143 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "IntersectionUtilities.h"
/* Given a cubic, find the convex hull described by the end and control points.
The hull may have 3 or 4 points. Cubics that degenerate into a point or line
are not considered.
The hull is computed by assuming that three points, if unique and non-linear,
form a triangle. The fourth point may replace one of the first three, may be
discarded if in the triangle or on an edge, or may be inserted between any of
the three to form a convex quadralateral.
The indices returned in order describe the convex hull.
*/
int convex_hull(const Cubic& cubic, char order[4]) {
size_t index;
// find top point
size_t yMin = 0;
for (index = 1; index < 4; ++index) {
if (cubic[yMin].y > cubic[index].y || (cubic[yMin].y == cubic[index].y
&& cubic[yMin].x > cubic[index].x)) {
yMin = index;
}
}
order[0] = yMin;
int midX = -1;
int backupYMin = -1;
for (int pass = 0; pass < 2; ++pass) {
for (index = 0; index < 4; ++index) {
if (index == yMin) {
continue;
}
// rotate line from (yMin, index) to axis
// see if remaining two points are both above or below
// use this to find mid
int mask = other_two(yMin, index);
int side1 = yMin ^ mask;
int side2 = index ^ mask;
Cubic rotPath;
if (!rotate(cubic, yMin, index, rotPath)) { // ! if cbc[yMin]==cbc[idx]
order[1] = side1;
order[2] = side2;
return 3;
}
int sides = side(rotPath[side1].y - rotPath[yMin].y);
sides ^= side(rotPath[side2].y - rotPath[yMin].y);
if (sides == 2) { // '2' means one remaining point <0, one >0
if (midX >= 0) {
printf("%s unexpected mid\n", __FUNCTION__); // there can be only one mid
}
midX = index;
} else if (sides == 0) { // '0' means both to one side or the other
backupYMin = index;
}
}
if (midX >= 0) {
break;
}
if (backupYMin < 0) {
break;
}
yMin = backupYMin;
backupYMin = -1;
}
if (midX < 0) {
midX = yMin ^ 3; // choose any other point
}
int mask = other_two(yMin, midX);
int least = yMin ^ mask;
int most = midX ^ mask;
order[0] = yMin;
order[1] = least;
// see if mid value is on same side of line (least, most) as yMin
Cubic midPath;
if (!rotate(cubic, least, most, midPath)) { // ! if cbc[least]==cbc[most]
order[2] = midX;
return 3;
}
int midSides = side(midPath[yMin].y - midPath[least].y);
midSides ^= side(midPath[midX].y - midPath[least].y);
if (midSides != 2) { // if mid point is not between
order[2] = most;
return 3; // result is a triangle
}
order[2] = midX;
order[3] = most;
return 4; // result is a quadralateral
}
/* Find the convex hull for cubics with the x-axis interval regularly spaced.
Cubics computed as distance functions are formed this way.
connectTo0[0], connectTo0[1] are the point indices that cubic[0] connects to.
connectTo3[0], connectTo3[1] are the point indices that cubic[3] connects to.
Returns true if cubic[1] to cubic[2] also forms part of the hull.
*/
bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]) {
double projectedY[4];
projectedY[0] = 0;
int index;
for (index = 1; index < 4; ++index) {
projectedY[index] = (cubic[index].y - cubic[0].y) * (3.0 / index);
}
int lower0Index = 1;
int upper0Index = 1;
for (index = 2; index < 4; ++index) {
if (approximately_greater_or_equal(projectedY[lower0Index], projectedY[index])) {
lower0Index = index;
}
if (approximately_lesser_or_equal(projectedY[upper0Index], projectedY[index])) {
upper0Index = index;
}
}
connectTo0[0] = lower0Index;
connectTo0[1] = upper0Index;
for (index = 0; index < 3; ++index) {
projectedY[index] = (cubic[3].y - cubic[index].y) * (3.0 / (3 - index));
}
projectedY[3] = 0;
int lower3Index = 2;
int upper3Index = 2;
for (index = 1; index > -1; --index) {
if (approximately_greater_or_equal(projectedY[lower3Index], projectedY[index])) {
lower3Index = index;
}
if (approximately_lesser_or_equal(projectedY[upper3Index], projectedY[index])) {
upper3Index = index;
}
}
connectTo3[0] = lower3Index;
connectTo3[1] = upper3Index;
return (1 << lower0Index | 1 << upper0Index
| 1 << lower3Index | 1 << upper3Index) == 0x0F;
}

View File

@ -1,468 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Intersection_Tests.h"
#include "IntersectionUtilities.h"
const Cubic convex[] = {
{{0, 0}, {2, 0}, {2, 1}, {0, 1}},
{{1, 0}, {1, 1}, {0, 1}, {0, 0}},
{{1, 1}, {0, 1}, {0, 0}, {1, 0}},
{{0, 1}, {0, 0}, {1, 0}, {1, 1}},
{{0, 0}, {10, 0}, {10, 10}, {5, 6}},
};
size_t convex_count = sizeof(convex) / sizeof(convex[0]);
const Cubic bowtie[] = {
{{0, 0}, {1, 1}, {1, 0}, {0, 1}},
{{1, 0}, {0, 1}, {1, 1}, {0, 0}},
{{1, 1}, {0, 0}, {0, 1}, {1, 0}},
{{0, 1}, {1, 0}, {0, 0}, {1, 1}},
};
size_t bowtie_count = sizeof(bowtie) / sizeof(bowtie[0]);
const Cubic arrow[] = {
{{0, 0}, {10, 0}, {10, 10}, {5, 4}},
{{10, 0}, {10, 10}, {5, 4}, {0, 0}},
{{10, 10}, {5, 4}, {0, 0}, {10, 0}},
{{5, 4}, {0, 0}, {10, 0}, {10, 10}},
};
size_t arrow_count = sizeof(arrow) / sizeof(arrow[0]);
const Cubic three[] = {
{{1, 0}, {1, 0}, {1, 1}, {0, 1}}, // 0 == 1
{{0, 0}, {1, 1}, {1, 1}, {0, 1}}, // 1 == 2
{{0, 0}, {1, 0}, {0, 1}, {0, 1}}, // 2 == 3
{{1, 0}, {1, 1}, {1, 0}, {0, 1}}, // 0 == 2
{{1, 0}, {1, 1}, {0, 1}, {1, 0}}, // 0 == 3
{{0, 0}, {1, 0}, {1, 1}, {1, 0}}, // 1 == 3
};
size_t three_count = sizeof(three) / sizeof(three[0]);
const Cubic triangle[] = {
{{0, 0}, {1, 0}, {2, 0}, {0, 1}}, // extra point on horz
{{1, 0}, {2, 0}, {0, 1}, {0, 0}},
{{2, 0}, {0, 1}, {0, 0}, {1, 0}},
{{0, 1}, {0, 0}, {1, 0}, {2, 0}},
{{0, 0}, {0, 1}, {0, 2}, {1, 1}}, // extra point on vert
{{0, 1}, {0, 2}, {1, 1}, {0, 0}},
{{0, 2}, {1, 1}, {0, 0}, {0, 1}},
{{1, 1}, {0, 0}, {0, 1}, {0, 2}},
{{0, 0}, {1, 1}, {2, 2}, {2, 0}}, // extra point on diag
{{1, 1}, {2, 2}, {2, 0}, {0, 0}},
{{2, 2}, {2, 0}, {0, 0}, {1, 1}},
{{2, 0}, {0, 0}, {1, 1}, {2, 2}},
{{0, 0}, {2, 0}, {2, 2}, {1, 1}}, // extra point on diag
{{2, 0}, {2, 2}, {1, 1}, {0, 0}},
{{2, 2}, {1, 1}, {0, 0}, {2, 0}},
{{1, 1}, {0, 0}, {2, 0}, {2, 2}},
};
size_t triangle_count = sizeof(triangle) / sizeof(triangle[0]);
const struct CubicDataSet {
const Cubic* data;
size_t size;
} cubicDataSet[] = {
{ three, three_count },
{ convex, convex_count },
{ bowtie, bowtie_count },
{ arrow, arrow_count },
{ triangle, triangle_count },
};
size_t cubicDataSet_count = sizeof(cubicDataSet) / sizeof(cubicDataSet[0]);
typedef double Matrix3x2[3][2];
static bool rotateToAxis(const _Point& a, const _Point& b, Matrix3x2& matrix) {
double dx = b.x - a.x;
double dy = b.y - a.y;
double length = sqrt(dx * dx + dy * dy);
if (length == 0) {
return false;
}
double invLength = 1 / length;
matrix[0][0] = dx * invLength;
matrix[1][0] = dy * invLength;
matrix[2][0] = 0;
matrix[0][1] = -dy * invLength;
matrix[1][1] = dx * invLength;
matrix[2][1] = 0;
return true;
}
static void transform(const Cubic& cubic, const Matrix3x2& matrix, Cubic& rotPath) {
for (int index = 0; index < 4; ++index) {
rotPath[index].x = cubic[index].x * matrix[0][0]
+ cubic[index].y * matrix[1][0] + matrix[2][0];
rotPath[index].y = cubic[index].x * matrix[0][1]
+ cubic[index].y * matrix[1][1] + matrix[2][1];
}
}
// brute force way to find convex hull:
// pick two points
// rotate all four until the two points are horizontal
// are the remaining two points both above or below the horizontal line?
// if so, the two points must be an edge of the convex hull
static int rotate_to_hull(const Cubic& cubic, char order[4], size_t idx, size_t inr) {
bool debug_rotate_to_hull = false;
int outsidePtSet[4];
memset(outsidePtSet, -1, sizeof(outsidePtSet));
for (int outer = 0; outer < 3; ++outer) {
for (int priorOuter = 0; priorOuter < outer; ++priorOuter) {
if (cubic[outer].approximatelyEqual(cubic[priorOuter])) {
goto skip;
}
}
for (int inner = outer + 1; inner < 4; ++inner) {
for (int priorInner = outer + 1; priorInner < inner; ++priorInner) {
if (cubic[inner].approximatelyEqual(cubic[priorInner])) {
goto skipInner;
}
}
if (cubic[outer].approximatelyEqual(cubic[inner])) {
continue;
}
Matrix3x2 matrix;
if (!rotateToAxis(cubic[outer], cubic[inner], matrix)) {
continue;
}
Cubic rotPath;
transform(cubic, matrix, rotPath);
int sides[3];
int zeroes;
zeroes = -1;
bzero(sides, sizeof(sides));
if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] src=(%g,%g) rot=", __FUNCTION__,
(int)idx, (int)inr, (int)outer, (int)inner,
cubic[inner].x, cubic[inner].y);
for (int index = 0; index < 4; ++index) {
if (debug_rotate_to_hull) SkDebugf("(%g,%g) ", rotPath[index].x, rotPath[index].y);
sides[side(rotPath[index].y - rotPath[inner].y)]++;
if (index != outer && index != inner
&& side(rotPath[index].y - rotPath[inner].y) == 1)
zeroes = index;
}
if (debug_rotate_to_hull) SkDebugf("sides=(%d,%d,%d)\n", sides[0], sides[1], sides[2]);
if (sides[0] && sides[2]) {
continue;
}
if (sides[1] == 3 && zeroes >= 0) {
// verify that third point is between outer, inner
// if either of remaining two equals outer or equal, pick lower
if (rotPath[zeroes].approximatelyEqual(rotPath[inner])
&& zeroes < inner) {
if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] zeroes < inner\n",
__FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
continue;
}
if (rotPath[zeroes].approximatelyEqual(rotPath[outer])
&& zeroes < outer) {
if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] zeroes < outer\n",
__FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
continue;
}
if (rotPath[zeroes].x < rotPath[inner].x
&& rotPath[zeroes].x < rotPath[outer].x) {
if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] zeroes < inner && outer\n",
__FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
continue;
}
if (rotPath[zeroes].x > rotPath[inner].x
&& rotPath[zeroes].x > rotPath[outer].x) {
if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] zeroes > inner && outer\n",
__FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
continue;
}
}
if (outsidePtSet[outer] < 0) {
outsidePtSet[outer] = inner;
} else {
if (outsidePtSet[inner] > 0) {
if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] too many rays from one point\n",
__FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
}
outsidePtSet[inner] = outer;
}
skipInner:
;
}
skip:
;
}
int totalSides = 0;
int first = 0;
for (; first < 4; ++first) {
if (outsidePtSet[first] >= 0) {
break;
}
}
if (first > 3) {
order[0] = 0;
return 1;
}
int next = first;
do {
order[totalSides++] = next;
next = outsidePtSet[next];
} while (next != -1 && next != first);
return totalSides;
}
int firstIndex = 0;
int firstInner = 0;
void ConvexHull_Test() {
for (size_t index = firstIndex; index < cubicDataSet_count; ++index) {
const CubicDataSet& set = cubicDataSet[index];
for (size_t inner = firstInner; inner < set.size; ++inner) {
const Cubic& cubic = set.data[inner];
char order[4], cmpOrder[4];
int cmp = rotate_to_hull(cubic, cmpOrder, index, inner);
if (cmp < 3) {
continue;
}
int result = convex_hull(cubic, order);
if (cmp != result) {
SkDebugf("%s [%d,%d] result=%d cmp=%d\n", __FUNCTION__,
(int)index, (int)inner, result, cmp);
continue;
}
// check for same indices
char pts = 0;
char cmpPts = 0;
int pt, bit;
for (pt = 0; pt < cmp; ++pt) {
if (pts & 1 << order[pt]) {
SkDebugf("%s [%d,%d] duplicate index in order: %d,%d,%d",
__FUNCTION__, (int)index, (int)inner,
order[0], order[1], order[2]);
if (cmp == 4) {
SkDebugf(",%d", order[3]);
}
SkDebugf("\n");
goto next;
}
if (cmpPts & 1 << cmpOrder[pt]) {
SkDebugf("%s [%d,%d] duplicate index in order: %d,%d,%d",
__FUNCTION__, (int)index, (int)inner,
cmpOrder[0], cmpOrder[1], cmpOrder[2]);
if (cmp == 4) {
SkDebugf(",%d", cmpOrder[3]);
}
SkDebugf("\n");
goto next;
}
pts |= 1 << order[pt];
cmpPts |= 1 << cmpOrder[pt];
}
for (bit = 0; bit < 4; ++bit) {
if (pts & 1 << bit) {
continue;
}
for (pt = 0; pt < cmp; ++pt) {
if (order[pt] == bit) {
continue;
}
if (cubic[order[pt]] == cubic[bit]) {
pts |= 1 << bit;
}
}
}
for (bit = 0; bit < 4; ++bit) {
if (cmpPts & 1 << bit) {
continue;
}
for (pt = 0; pt < cmp; ++pt) {
if (cmpOrder[pt] == bit) {
continue;
}
if (cubic[cmpOrder[pt]] == cubic[bit]) {
cmpPts |= 1 << bit;
}
}
}
if (pts != cmpPts) {
SkDebugf("%s [%d,%d] mismatch indices: order=%d,%d,%d",
__FUNCTION__, (int)index, (int)inner,
order[0], order[1], order[2]);
if (cmp == 4) {
SkDebugf(",%d", order[3]);
}
SkDebugf(" cmpOrder=%d,%d,%d", cmpOrder[0], cmpOrder[1], cmpOrder[2]);
if (cmp == 4) {
SkDebugf(",%d", cmpOrder[3]);
}
SkDebugf("\n");
continue;
}
if (cmp == 4) { // check for bow ties
int match = 0;
while (cmpOrder[match] != order[0]) {
++match;
}
if (cmpOrder[match ^ 2] != order[2]) {
SkDebugf("%s [%d,%d] bowtie mismatch: order=%d,%d,%d,%d"
" cmpOrder=%d,%d,%d,%d\n",
__FUNCTION__, (int)index, (int)inner,
order[0], order[1], order[2], order[3],
cmpOrder[0], cmpOrder[1], cmpOrder[2], cmpOrder[3]);
}
}
next:
;
}
}
}
const double a = 1.0/3;
const double b = 2.0/3;
const Cubic x_cubic[] = {
{{0, 0}, {a, 0}, {b, 0}, {1, 0}}, // 0
{{0, 0}, {a, 0}, {b, 0}, {1, 1}}, // 1
{{0, 0}, {a, 0}, {b, 1}, {1, 0}}, // 2
{{0, 0}, {a, 0}, {b, 1}, {1, 1}}, // 3
{{0, 0}, {a, 1}, {b, 0}, {1, 0}}, // 4
{{0, 0}, {a, 1}, {b, 0}, {1, 1}}, // 5
{{0, 0}, {a, 1}, {b, 1}, {1, 0}}, // 6
{{0, 0}, {a, 1}, {b, 1}, {1, 1}}, // 7
{{0, 1}, {a, 0}, {b, 0}, {1, 0}}, // 8
{{0, 1}, {a, 0}, {b, 0}, {1, 1}}, // 9
{{0, 1}, {a, 0}, {b, 1}, {1, 0}}, // 10
{{0, 1}, {a, 0}, {b, 1}, {1, 1}}, // 11
{{0, 1}, {a, 1}, {b, 0}, {1, 0}}, // 12
{{0, 1}, {a, 1}, {b, 0}, {1, 1}}, // 13
{{0, 1}, {a, 1}, {b, 1}, {1, 0}}, // 14
{{0, 1}, {a, 1}, {b, 1}, {1, 1}}, // 15
};
size_t x_cubic_count = sizeof(x_cubic) / sizeof(x_cubic[0]);
static int first_x_test = 0;
void ConvexHull_X_Test() {
for (size_t index = first_x_test; index < x_cubic_count; ++index) {
const Cubic& cubic = x_cubic[index];
char connectTo0[2] = {-1, -1};
char connectTo3[2] = {-1, -1};
convex_x_hull(cubic, connectTo0, connectTo3);
int idx, cmp;
for (idx = 0; idx < 2; ++idx) {
if (connectTo0[idx] >= 1 && connectTo0[idx] < 4) {
continue;
} else {
SkDebugf("%s connectTo0[idx]=%d", __FUNCTION__, connectTo0[idx]);
}
if (connectTo3[idx] >= 0 && connectTo3[idx] < 3) {
continue;
} else {
SkDebugf("%s connectTo3[idx]=%d", __FUNCTION__, connectTo3[idx]);
}
goto nextTest;
}
char rOrder[4];
char cmpOrder[4];
cmp = rotate_to_hull(cubic, cmpOrder, index, 0);
if (index == 0 || index == 15) {
// FIXME: make rotate_to_hull work for degenerate 2 edge hull cases
cmpOrder[0] = 0;
cmpOrder[1] = 3;
cmp = 2;
}
if (cmp < 3) {
// FIXME: make rotate_to_hull work for index == 3 etc
continue;
}
for (idx = 0; idx < cmp; ++idx) {
if (cmpOrder[idx] == 0) {
rOrder[0] = cmpOrder[(idx + 1) % cmp];
rOrder[1] = cmpOrder[(idx + cmp - 1) % cmp];
} else if (cmpOrder[idx] == 3) {
rOrder[2] = cmpOrder[(idx + 1) % cmp];
rOrder[3] = cmpOrder[(idx + cmp - 1) % cmp];
}
}
if (connectTo0[0] != connectTo0[1]) {
if (rOrder[0] == rOrder[1]) {
SkDebugf("%s [%d] (1) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
__FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
connectTo3[0], connectTo3[1],
rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
continue;
}
int unused = 6 - connectTo0[0] - connectTo0[1];
int rUnused = 6 - rOrder[0] - rOrder[1];
if (unused != rUnused) {
SkDebugf("%s [%d] (2) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
__FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
connectTo3[0], connectTo3[1],
rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
continue;
}
} else {
if (rOrder[0] != rOrder[1]) {
SkDebugf("%s [%d] (3) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
__FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
connectTo3[0], connectTo3[1],
rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
continue;
}
if (connectTo0[0] != rOrder[0]) {
SkDebugf("%s [%d] (4) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
__FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
connectTo3[0], connectTo3[1],
rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
continue;
}
}
if (connectTo3[0] != connectTo3[1]) {
if (rOrder[2] == rOrder[3]) {
SkDebugf("%s [%d] (5) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
__FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
connectTo3[0], connectTo3[1],
rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
continue;
}
int unused = 6 - connectTo3[0] - connectTo3[1];
int rUnused = 6 - rOrder[2] - rOrder[3];
if (unused != rUnused) {
SkDebugf("%s [%d] (6) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
__FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
connectTo3[0], connectTo3[1],
rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
continue;
}
} else {
if (rOrder[2] != rOrder[3]) {
SkDebugf("%s [%d] (7) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
__FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
connectTo3[0], connectTo3[1],
rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
continue;
}
if (connectTo3[1] != rOrder[3]) {
SkDebugf("%s [%d] (8) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
__FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
connectTo3[0], connectTo3[1],
rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
continue;
}
}
nextTest:
;
}
}

View File

@ -1,400 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
// http://metamerist.com/cbrt/CubeRoot.cpp
//
#include <math.h>
#include "CubicUtilities.h"
#define TEST_ALTERNATIVES 0
#if TEST_ALTERNATIVES
typedef float (*cuberootfnf) (float);
typedef double (*cuberootfnd) (double);
// estimate bits of precision (32-bit float case)
inline int bits_of_precision(float a, float b)
{
const double kd = 1.0 / log(2.0);
if (a==b)
return 23;
const double kdmin = pow(2.0, -23.0);
double d = fabs(a-b);
if (d < kdmin)
return 23;
return int(-log(d)*kd);
}
// estiamte bits of precision (64-bit double case)
inline int bits_of_precision(double a, double b)
{
const double kd = 1.0 / log(2.0);
if (a==b)
return 52;
const double kdmin = pow(2.0, -52.0);
double d = fabs(a-b);
if (d < kdmin)
return 52;
return int(-log(d)*kd);
}
// cube root via x^(1/3)
static float pow_cbrtf(float x)
{
return (float) pow(x, 1.0f/3.0f);
}
// cube root via x^(1/3)
static double pow_cbrtd(double x)
{
return pow(x, 1.0/3.0);
}
// cube root approximation using bit hack for 32-bit float
static float cbrt_5f(float f)
{
unsigned int* p = (unsigned int *) &f;
*p = *p/3 + 709921077;
return f;
}
#endif
// cube root approximation using bit hack for 64-bit float
// adapted from Kahan's cbrt
static double cbrt_5d(double d)
{
const unsigned int B1 = 715094163;
double t = 0.0;
unsigned int* pt = (unsigned int*) &t;
unsigned int* px = (unsigned int*) &d;
pt[1]=px[1]/3+B1;
return t;
}
#if TEST_ALTERNATIVES
// cube root approximation using bit hack for 64-bit float
// adapted from Kahan's cbrt
#if 0
static double quint_5d(double d)
{
return sqrt(sqrt(d));
const unsigned int B1 = 71509416*5/3;
double t = 0.0;
unsigned int* pt = (unsigned int*) &t;
unsigned int* px = (unsigned int*) &d;
pt[1]=px[1]/5+B1;
return t;
}
#endif
// iterative cube root approximation using Halley's method (float)
static float cbrta_halleyf(const float a, const float R)
{
const float a3 = a*a*a;
const float b= a * (a3 + R + R) / (a3 + a3 + R);
return b;
}
#endif
// iterative cube root approximation using Halley's method (double)
static double cbrta_halleyd(const double a, const double R)
{
const double a3 = a*a*a;
const double b= a * (a3 + R + R) / (a3 + a3 + R);
return b;
}
#if TEST_ALTERNATIVES
// iterative cube root approximation using Newton's method (float)
static float cbrta_newtonf(const float a, const float x)
{
// return (1.0 / 3.0) * ((a + a) + x / (a * a));
return a - (1.0f / 3.0f) * (a - x / (a*a));
}
// iterative cube root approximation using Newton's method (double)
static double cbrta_newtond(const double a, const double x)
{
return (1.0/3.0) * (x / (a*a) + 2*a);
}
// cube root approximation using 1 iteration of Halley's method (double)
static double halley_cbrt1d(double d)
{
double a = cbrt_5d(d);
return cbrta_halleyd(a, d);
}
// cube root approximation using 1 iteration of Halley's method (float)
static float halley_cbrt1f(float d)
{
float a = cbrt_5f(d);
return cbrta_halleyf(a, d);
}
// cube root approximation using 2 iterations of Halley's method (double)
static double halley_cbrt2d(double d)
{
double a = cbrt_5d(d);
a = cbrta_halleyd(a, d);
return cbrta_halleyd(a, d);
}
#endif
// cube root approximation using 3 iterations of Halley's method (double)
static double halley_cbrt3d(double d)
{
double a = cbrt_5d(d);
a = cbrta_halleyd(a, d);
a = cbrta_halleyd(a, d);
return cbrta_halleyd(a, d);
}
#if TEST_ALTERNATIVES
// cube root approximation using 2 iterations of Halley's method (float)
static float halley_cbrt2f(float d)
{
float a = cbrt_5f(d);
a = cbrta_halleyf(a, d);
return cbrta_halleyf(a, d);
}
// cube root approximation using 1 iteration of Newton's method (double)
static double newton_cbrt1d(double d)
{
double a = cbrt_5d(d);
return cbrta_newtond(a, d);
}
// cube root approximation using 2 iterations of Newton's method (double)
static double newton_cbrt2d(double d)
{
double a = cbrt_5d(d);
a = cbrta_newtond(a, d);
return cbrta_newtond(a, d);
}
// cube root approximation using 3 iterations of Newton's method (double)
static double newton_cbrt3d(double d)
{
double a = cbrt_5d(d);
a = cbrta_newtond(a, d);
a = cbrta_newtond(a, d);
return cbrta_newtond(a, d);
}
// cube root approximation using 4 iterations of Newton's method (double)
static double newton_cbrt4d(double d)
{
double a = cbrt_5d(d);
a = cbrta_newtond(a, d);
a = cbrta_newtond(a, d);
a = cbrta_newtond(a, d);
return cbrta_newtond(a, d);
}
// cube root approximation using 2 iterations of Newton's method (float)
static float newton_cbrt1f(float d)
{
float a = cbrt_5f(d);
return cbrta_newtonf(a, d);
}
// cube root approximation using 2 iterations of Newton's method (float)
static float newton_cbrt2f(float d)
{
float a = cbrt_5f(d);
a = cbrta_newtonf(a, d);
return cbrta_newtonf(a, d);
}
// cube root approximation using 3 iterations of Newton's method (float)
static float newton_cbrt3f(float d)
{
float a = cbrt_5f(d);
a = cbrta_newtonf(a, d);
a = cbrta_newtonf(a, d);
return cbrta_newtonf(a, d);
}
// cube root approximation using 4 iterations of Newton's method (float)
static float newton_cbrt4f(float d)
{
float a = cbrt_5f(d);
a = cbrta_newtonf(a, d);
a = cbrta_newtonf(a, d);
a = cbrta_newtonf(a, d);
return cbrta_newtonf(a, d);
}
static double TestCubeRootf(const char* szName, cuberootfnf cbrt, double rA, double rB, int rN)
{
const int N = rN;
float dd = float((rB-rA) / N);
// calculate 1M numbers
int i=0;
float d = (float) rA;
double s = 0.0;
for(d=(float) rA, i=0; i<N; i++, d += dd)
{
s += cbrt(d);
}
double bits = 0.0;
double worstx=0.0;
double worsty=0.0;
int minbits=64;
for(d=(float) rA, i=0; i<N; i++, d += dd)
{
float a = cbrt((float) d);
float b = (float) pow((double) d, 1.0/3.0);
int bc = bits_of_precision(a, b);
bits += bc;
if (b > 1.0e-6)
{
if (bc < minbits)
{
minbits = bc;
worstx = d;
worsty = a;
}
}
}
bits /= N;
printf(" %3d mbp %6.3f abp\n", minbits, bits);
return s;
}
static double TestCubeRootd(const char* szName, cuberootfnd cbrt, double rA, double rB, int rN)
{
const int N = rN;
double dd = (rB-rA) / N;
int i=0;
double s = 0.0;
double d = 0.0;
for(d=rA, i=0; i<N; i++, d += dd)
{
s += cbrt(d);
}
double bits = 0.0;
double worstx = 0.0;
double worsty = 0.0;
int minbits = 64;
for(d=rA, i=0; i<N; i++, d += dd)
{
double a = cbrt(d);
double b = pow(d, 1.0/3.0);
int bc = bits_of_precision(a, b); // min(53, count_matching_bitsd(a, b) - 12);
bits += bc;
if (b > 1.0e-6)
{
if (bc < minbits)
{
bits_of_precision(a, b);
minbits = bc;
worstx = d;
worsty = a;
}
}
}
bits /= N;
printf(" %3d mbp %6.3f abp\n", minbits, bits);
return s;
}
static int _tmain()
{
// a million uniform steps through the range from 0.0 to 1.0
// (doing uniform steps in the log scale would be better)
double a = 0.0;
double b = 1.0;
int n = 1000000;
printf("32-bit float tests\n");
printf("----------------------------------------\n");
TestCubeRootf("cbrt_5f", cbrt_5f, a, b, n);
TestCubeRootf("pow", pow_cbrtf, a, b, n);
TestCubeRootf("halley x 1", halley_cbrt1f, a, b, n);
TestCubeRootf("halley x 2", halley_cbrt2f, a, b, n);
TestCubeRootf("newton x 1", newton_cbrt1f, a, b, n);
TestCubeRootf("newton x 2", newton_cbrt2f, a, b, n);
TestCubeRootf("newton x 3", newton_cbrt3f, a, b, n);
TestCubeRootf("newton x 4", newton_cbrt4f, a, b, n);
printf("\n\n");
printf("64-bit double tests\n");
printf("----------------------------------------\n");
TestCubeRootd("cbrt_5d", cbrt_5d, a, b, n);
TestCubeRootd("pow", pow_cbrtd, a, b, n);
TestCubeRootd("halley x 1", halley_cbrt1d, a, b, n);
TestCubeRootd("halley x 2", halley_cbrt2d, a, b, n);
TestCubeRootd("halley x 3", halley_cbrt3d, a, b, n);
TestCubeRootd("newton x 1", newton_cbrt1d, a, b, n);
TestCubeRootd("newton x 2", newton_cbrt2d, a, b, n);
TestCubeRootd("newton x 3", newton_cbrt3d, a, b, n);
TestCubeRootd("newton x 4", newton_cbrt4d, a, b, n);
printf("\n\n");
return 0;
}
#endif
double cube_root(double x) {
if (approximately_zero_cubed(x)) {
return 0;
}
double result = halley_cbrt3d(fabs(x));
if (x < 0) {
result = -result;
}
return result;
}
#if TEST_ALTERNATIVES
// http://bytes.com/topic/c/answers/754588-tips-find-cube-root-program-using-c
/* cube root */
int icbrt(int n) {
int t=0, x=(n+2)/3; /* works for n=0 and n>=1 */
for(; t!=x;) {
int x3=x*x*x;
t=x;
x*=(2*n + x3);
x/=(2*x3 + n);
}
return x ; /* always(?) equal to floor(n^(1/3)) */
}
#endif

View File

@ -1,89 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "LineParameters.h"
// return false if unable to clip (e.g., unable to create implicit line)
// caller should subdivide, or create degenerate if the values are too small
bool bezier_clip(const Cubic& cubic1, const Cubic& cubic2, double& minT, double& maxT) {
minT = 1;
maxT = 0;
// determine normalized implicit line equation for pt[0] to pt[3]
// of the form ax + by + c = 0, where a*a + b*b == 1
// find the implicit line equation parameters
LineParameters endLine;
endLine.cubicEndPoints(cubic1);
if (!endLine.normalize()) {
printf("line cannot be normalized: need more code here\n");
return false;
}
double distance[2];
distance[0] = endLine.controlPtDistance(cubic1, 1);
distance[1] = endLine.controlPtDistance(cubic1, 2);
// find fat line
double top = distance[0];
double bottom = distance[1];
if (top > bottom) {
SkTSwap(top, bottom);
}
if (top * bottom >= 0) {
const double scale = 3/4.0; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf (13)
if (top < 0) {
top *= scale;
bottom = 0;
} else {
top = 0;
bottom *= scale;
}
} else {
const double scale = 4/9.0; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf (15)
top *= scale;
bottom *= scale;
}
// compute intersecting candidate distance
Cubic distance2y; // points with X of (0, 1/3, 2/3, 1)
endLine.cubicDistanceY(cubic2, distance2y);
int flags = 0;
if (approximately_lesser_or_equal(distance2y[0].y, top)) {
flags |= kFindTopMin;
} else if (approximately_greater_or_equal(distance2y[0].y, bottom)) {
flags |= kFindBottomMin;
} else {
minT = 0;
}
if (approximately_lesser_or_equal(distance2y[3].y, top)) {
flags |= kFindTopMax;
} else if (approximately_greater_or_equal(distance2y[3].y, bottom)) {
flags |= kFindBottomMax;
} else {
maxT = 1;
}
// Find the intersection of distance convex hull and fat line.
char to_0[2];
char to_3[2];
bool do_1_2_edge = convex_x_hull(distance2y, to_0, to_3);
x_at(distance2y[0], distance2y[to_0[0]], top, bottom, flags, minT, maxT);
if (to_0[0] != to_0[1]) {
x_at(distance2y[0], distance2y[to_0[1]], top, bottom, flags, minT, maxT);
}
x_at(distance2y[to_3[0]], distance2y[3], top, bottom, flags, minT, maxT);
if (to_3[0] != to_3[1]) {
x_at(distance2y[to_3[1]], distance2y[3], top, bottom, flags, minT, maxT);
}
if (do_1_2_edge) {
x_at(distance2y[1], distance2y[2], top, bottom, flags, minT, maxT);
}
return minT < maxT; // returns false if distance shows no intersection
}

View File

@ -1,32 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CubicIntersection_TestData.h"
#include "Intersection_Tests.h"
void CubicBezierClip_Test() {
for (size_t index = 0; index < tests_count; ++index) {
const Cubic& cubic1 = tests[index][0];
const Cubic& cubic2 = tests[index][1];
Cubic reduce1, reduce2;
int order1 = reduceOrder(cubic1, reduce1, kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_TreatAsFill);
int order2 = reduceOrder(cubic2, reduce2, kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order1 < 4) {
SkDebugf("%s [%d] cubic1 order=%d\n", __FUNCTION__, (int) index, order1);
}
if (order2 < 4) {
SkDebugf("%s [%d] cubic2 order=%d\n", __FUNCTION__, (int) index, order2);
}
if (order1 == 4 && order2 == 4) {
double minT = 0;
double maxT = 1;
bezier_clip(reduce1, reduce2, minT, maxT);
}
}
}

View File

@ -1,66 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Extrema.h"
static int isBoundedByEndPoints(double a, double b, double c, double d)
{
return between(a, b, d) && between(a, 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]);
}
}

View File

@ -1,165 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CubicUtilities.h"
#include "CurveIntersection.h"
#include "Intersections.h"
#include "IntersectionUtilities.h"
#include "LineIntersection.h"
static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf see Multiple intersections
class CubicIntersections : public Intersections {
public:
CubicIntersections(const Cubic& c1, const Cubic& c2, Intersections& i)
: cubic1(c1)
, cubic2(c2)
, intersections(i)
, depth(0)
, splits(0) {
}
bool intersect() {
double minT1, minT2, maxT1, maxT2;
if (!bezier_clip(cubic2, cubic1, minT1, maxT1)) {
return false;
}
if (!bezier_clip(cubic1, cubic2, minT2, maxT2)) {
return false;
}
int split;
if (maxT1 - minT1 < maxT2 - minT2) {
intersections.swap();
minT2 = 0;
maxT2 = 1;
split = maxT1 - minT1 > tClipLimit;
} else {
minT1 = 0;
maxT1 = 1;
split = (maxT2 - minT2 > tClipLimit) << 1;
}
return chop(minT1, maxT1, minT2, maxT2, split);
}
protected:
bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
Cubic smaller, larger;
// FIXME: carry last subdivide and reduceOrder result with cubic
sub_divide(cubic1, minT1, maxT1, intersections.swapped() ? larger : smaller);
sub_divide(cubic2, minT2, maxT2, intersections.swapped() ? smaller : larger);
Cubic smallResult;
if (reduceOrder(smaller, smallResult, kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_TreatAsFill) <= 2) {
Cubic largeResult;
if (reduceOrder(larger, largeResult, kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_TreatAsFill) <= 2) {
const _Line& smallLine = (const _Line&) smallResult;
const _Line& largeLine = (const _Line&) largeResult;
Intersections lineTs;
// FIXME: this doesn't detect or deal with coincident lines
if (!::intersect(smallLine, largeLine, lineTs)) {
return false;
}
if (intersections.swapped()) {
lineTs.fT[0][0] = interp(minT2, maxT2, lineTs.fT[0][0]);
lineTs.fT[1][0] = interp(minT1, maxT1, lineTs.fT[1][0]);
} else {
lineTs.fT[0][0] = interp(minT1, maxT1, lineTs.fT[0][0]);
lineTs.fT[1][0] = interp(minT2, maxT2, lineTs.fT[1][0]);
}
_Point pt;
xy_at_t(cubic1, lineTs.fT[0][0], pt.x, pt.y);
intersections.insert(lineTs.fT[0][0], lineTs.fT[1][0], pt);
return true;
}
}
double minT, maxT;
if (!bezier_clip(smaller, larger, minT, maxT)) {
if (minT == maxT) {
if (intersections.swapped()) {
minT1 = (minT1 + maxT1) / 2;
minT2 = interp(minT2, maxT2, minT);
} else {
minT1 = interp(minT1, maxT1, minT);
minT2 = (minT2 + maxT2) / 2;
}
_Point pt;
xy_at_t(cubic1, minT1, pt.x, pt.y);
intersections.insert(minT1, minT2, pt);
return true;
}
return false;
}
int split;
if (intersections.swapped()) {
double newMinT1 = interp(minT1, maxT1, minT);
double newMaxT1 = interp(minT1, maxT1, maxT);
split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1;
#define VERBOSE 0
#if VERBOSE
printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n",
__FUNCTION__, depth, splits, newMinT1, newMaxT1, minT1, maxT1,
split);
#endif
minT1 = newMinT1;
maxT1 = newMaxT1;
} else {
double newMinT2 = interp(minT2, maxT2, minT);
double newMaxT2 = interp(minT2, maxT2, maxT);
split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit;
#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);
#endif
minT2 = newMinT2;
maxT2 = newMaxT2;
}
return chop(minT1, maxT1, minT2, maxT2, split);
}
bool chop(double minT1, double maxT1, double minT2, double maxT2, int split) {
++depth;
intersections.swap();
if (split) {
++splits;
if (split & 2) {
double middle1 = (maxT1 + minT1) / 2;
intersect(minT1, middle1, minT2, maxT2);
intersect(middle1, maxT1, minT2, maxT2);
} else {
double middle2 = (maxT2 + minT2) / 2;
intersect(minT1, maxT1, minT2, middle2);
intersect(minT1, maxT1, middle2, maxT2);
}
--splits;
intersections.swap();
--depth;
return intersections.intersected();
}
bool result = intersect(minT1, maxT1, minT2, maxT2);
intersections.swap();
--depth;
return result;
}
private:
const Cubic& cubic1;
const Cubic& cubic2;
Intersections& intersections;
int depth;
int splits;
};
bool intersect(const Cubic& c1, const Cubic& c2, Intersections& i) {
CubicIntersections c(c1, c2, i);
return c.intersect();
}

View File

@ -1,471 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CubicUtilities.h"
#include "CurveIntersection.h"
#include "Intersections.h"
#include "IntersectionUtilities.h"
#include "LineIntersection.h"
#include "LineUtilities.h"
#include "QuadraticUtilities.h"
#include "TSearch.h"
#if 0
#undef ONE_OFF_DEBUG
#define ONE_OFF_DEBUG 0
#endif
#if ONE_OFF_DEBUG
static const double tLimits1[2][2] = {{0.36, 0.37}, {0.63, 0.64}};
static const double tLimits2[2][2] = {{-0.865211397, -0.865215212}, {-0.865207696, -0.865208078}};
#endif
#define DEBUG_QUAD_PART 0
#define SWAP_TOP_DEBUG 0
static int quadPart(const Cubic& cubic, double tStart, double tEnd, Quadratic& simple) {
Cubic part;
sub_divide(cubic, tStart, tEnd, part);
Quadratic quad;
demote_cubic_to_quad(part, quad);
// FIXME: should reduceOrder be looser in this use case if quartic is going to blow up on an
// extremely shallow quadratic?
int order = reduceOrder(quad, simple, kReduceOrder_TreatAsFill);
#if DEBUG_QUAD_PART
SkDebugf("%s cubic=(%1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g) t=(%1.17g,%1.17g)\n",
__FUNCTION__, cubic[0].x, cubic[0].y, cubic[1].x, cubic[1].y, cubic[2].x, cubic[2].y,
cubic[3].x, cubic[3].y, tStart, tEnd);
SkDebugf("%s part=(%1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g)"
" quad=(%1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g)\n", __FUNCTION__, part[0].x, part[0].y,
part[1].x, part[1].y, part[2].x, part[2].y, part[3].x, part[3].y, quad[0].x, quad[0].y,
quad[1].x, quad[1].y, quad[2].x, quad[2].y);
SkDebugf("%s simple=(%1.17g,%1.17g", __FUNCTION__, simple[0].x, simple[0].y);
if (order > 1) {
SkDebugf(" %1.17g,%1.17g", simple[1].x, simple[1].y);
}
if (order > 2) {
SkDebugf(" %1.17g,%1.17g", simple[2].x, simple[2].y);
}
SkDebugf(")\n");
SkASSERT(order < 4 && order > 0);
#endif
return order;
}
static void intersectWithOrder(const Quadratic& simple1, int order1, const Quadratic& simple2,
int order2, Intersections& i) {
if (order1 == 3 && order2 == 3) {
intersect2(simple1, simple2, i);
} else if (order1 <= 2 && order2 <= 2) {
intersect((const _Line&) simple1, (const _Line&) simple2, i);
} else if (order1 == 3 && order2 <= 2) {
intersect(simple1, (const _Line&) simple2, i);
} else {
SkASSERT(order1 <= 2 && order2 == 3);
intersect(simple2, (const _Line&) simple1, i);
for (int s = 0; s < i.fUsed; ++s) {
SkTSwap(i.fT[0][s], i.fT[1][s]);
}
}
}
// this flavor centers potential intersections recursively. In contrast, '2' may inadvertently
// chase intersections near quadratic ends, requiring odd hacks to find them.
static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic& cubic2,
double t2s, double t2e, double precisionScale, Intersections& i) {
i.upDepth();
bool result = false;
Cubic c1, c2;
sub_divide(cubic1, t1s, t1e, c1);
sub_divide(cubic2, t2s, t2e, c2);
SkTDArray<double> ts1;
// OPTIMIZE: if c1 == c2, call once (happens when detecting self-intersection)
cubic_to_quadratics(c1, calcPrecision(c1) * precisionScale, ts1);
SkTDArray<double> ts2;
cubic_to_quadratics(c2, calcPrecision(c2) * precisionScale, ts2);
double t1Start = t1s;
int ts1Count = ts1.count();
for (int i1 = 0; i1 <= ts1Count; ++i1) {
const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1;
const double t1 = t1s + (t1e - t1s) * tEnd1;
Quadratic s1;
int o1 = quadPart(cubic1, t1Start, t1, s1);
double t2Start = t2s;
int ts2Count = ts2.count();
for (int i2 = 0; i2 <= ts2Count; ++i2) {
const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1;
const double t2 = t2s + (t2e - t2s) * tEnd2;
if (cubic1 == cubic2 && t1Start >= t2Start) {
t2Start = t2;
continue;
}
Quadratic s2;
int o2 = quadPart(cubic2, t2Start, t2, s2);
#if ONE_OFF_DEBUG
char tab[] = " ";
if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1
&& tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) {
Cubic cSub1, cSub2;
sub_divide(cubic1, t1Start, t1, cSub1);
sub_divide(cubic2, t2Start, t2, cSub2);
SkDebugf("%.*s %s t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)", i.depth()*2, tab, __FUNCTION__,
t1Start, t1, t2Start, t2);
Intersections xlocals;
intersectWithOrder(s1, o1, s2, o2, xlocals);
SkDebugf(" xlocals.fUsed=%d\n", xlocals.used());
}
#endif
Intersections locals;
intersectWithOrder(s1, o1, s2, o2, locals);
double coStart[2] = { -1 };
_Point coPoint;
int tCount = locals.used();
for (int tIdx = 0; tIdx < tCount; ++tIdx) {
double to1 = t1Start + (t1 - t1Start) * locals.fT[0][tIdx];
double to2 = t2Start + (t2 - t2Start) * locals.fT[1][tIdx];
// if the computed t is not sufficiently precise, iterate
_Point p1 = xy_at_t(cubic1, to1);
_Point p2 = xy_at_t(cubic2, to2);
if (p1.approximatelyEqual(p2)) {
if (locals.fIsCoincident[0] & 1 << tIdx) {
if (coStart[0] < 0) {
coStart[0] = to1;
coStart[1] = to2;
coPoint = p1;
} else {
i.insertCoincidentPair(coStart[0], to1, coStart[1], to2, coPoint, p1);
coStart[0] = -1;
}
result = true;
} else if (cubic1 != cubic2 || !approximately_equal(to1, to2)) {
if (i.swapped()) { // FIXME: insert should respect swap
i.insert(to2, to1, p1);
} else {
i.insert(to1, to2, p1);
}
result = true;
}
} else {
double offset = precisionScale / 16; // FIME: const is arbitrary -- test & refine
#if 1
double c1Bottom = tIdx == 0 ? 0 :
(t1Start + (t1 - t1Start) * locals.fT[0][tIdx - 1] + to1) / 2;
double c1Min = SkTMax(c1Bottom, to1 - offset);
double c1Top = tIdx == tCount - 1 ? 1 :
(t1Start + (t1 - t1Start) * locals.fT[0][tIdx + 1] + to1) / 2;
double c1Max = SkTMin(c1Top, to1 + offset);
double c2Min = SkTMax(0., to2 - offset);
double c2Max = SkTMin(1., to2 + offset);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 1 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__,
c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
&& c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
&& to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
&& c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
&& to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
SkDebugf("%.*s %s 1 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
" 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
i.depth()*2, tab, __FUNCTION__, c1Bottom, c1Top, 0., 1.,
to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
SkDebugf("%.*s %s 1 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
" c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max);
#endif
intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 1 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(),
i.used() > 0 ? i.fT[0][i.used() - 1] : -1);
#endif
if (tCount > 1) {
c1Min = SkTMax(0., to1 - offset);
c1Max = SkTMin(1., to1 + offset);
double c2Bottom = tIdx == 0 ? to2 :
(t2Start + (t2 - t2Start) * locals.fT[1][tIdx - 1] + to2) / 2;
double c2Top = tIdx == tCount - 1 ? to2 :
(t2Start + (t2 - t2Start) * locals.fT[1][tIdx + 1] + to2) / 2;
if (c2Bottom > c2Top) {
SkTSwap(c2Bottom, c2Top);
}
if (c2Bottom == to2) {
c2Bottom = 0;
}
if (c2Top == to2) {
c2Top = 1;
}
c2Min = SkTMax(c2Bottom, to2 - offset);
c2Max = SkTMin(c2Top, to2 + offset);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 2 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__,
c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
&& c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
&& to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
&& c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
&& to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
SkDebugf("%.*s %s 2 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
" 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
SkDebugf("%.*s %s 2 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
" c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max);
#endif
intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 2 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(),
i.used() > 0 ? i.fT[0][i.used() - 1] : -1);
#endif
c1Min = SkTMax(c1Bottom, to1 - offset);
c1Max = SkTMin(c1Top, to1 + offset);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 3 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__,
c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
&& c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
&& to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
&& c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
&& to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
SkDebugf("%.*s %s 3 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
" 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
SkDebugf("%.*s %s 3 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
" c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max);
#endif
intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
#if ONE_OFF_DEBUG
SkDebugf("%.*s %s 3 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(),
i.used() > 0 ? i.fT[0][i.used() - 1] : -1);
#endif
}
#else
double c1Bottom = tIdx == 0 ? 0 :
(t1Start + (t1 - t1Start) * locals.fT[0][tIdx - 1] + to1) / 2;
double c1Min = SkTMax(c1Bottom, to1 - offset);
double c1Top = tIdx == tCount - 1 ? 1 :
(t1Start + (t1 - t1Start) * locals.fT[0][tIdx + 1] + to1) / 2;
double c1Max = SkTMin(c1Top, to1 + offset);
double c2Bottom = tIdx == 0 ? to2 :
(t2Start + (t2 - t2Start) * locals.fT[1][tIdx - 1] + to2) / 2;
double c2Top = tIdx == tCount - 1 ? to2 :
(t2Start + (t2 - t2Start) * locals.fT[1][tIdx + 1] + to2) / 2;
if (c2Bottom > c2Top) {
SkTSwap(c2Bottom, c2Top);
}
if (c2Bottom == to2) {
c2Bottom = 0;
}
if (c2Top == to2) {
c2Top = 1;
}
double c2Min = SkTMax(c2Bottom, to2 - offset);
double c2Max = SkTMin(c2Top, to2 + offset);
#if ONE_OFF_DEBUG
SkDebugf("%s contains1=%d/%d contains2=%d/%d\n", __FUNCTION__,
c1Min <= 0.210357794 && 0.210357794 <= c1Max
&& c2Min <= 0.223476406 && 0.223476406 <= c2Max,
to1 - offset <= 0.210357794 && 0.210357794 <= to1 + offset
&& to2 - offset <= 0.223476406 && 0.223476406 <= to2 + offset,
c1Min <= 0.211324707 && 0.211324707 <= c1Max
&& c2Min <= 0.211327209 && 0.211327209 <= c2Max,
to1 - offset <= 0.211324707 && 0.211324707 <= to1 + offset
&& to2 - offset <= 0.211327209 && 0.211327209 <= to2 + offset);
SkDebugf("%s c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
" 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
__FUNCTION__, c1Bottom, c1Top, c2Bottom, c2Top,
to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
SkDebugf("%s to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
" c2Max=%1.9g\n", __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max);
#endif
#endif
intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
// TODO: if no intersection is found, either quadratics intersected where
// cubics did not, or the intersection was missed. In the former case, expect
// the quadratics to be nearly parallel at the point of intersection, and check
// for that.
}
}
SkASSERT(coStart[0] == -1);
t2Start = t2;
}
t1Start = t1;
}
i.downDepth();
return result;
}
#if 0
#define LINE_FRACTION (1.0 / gPrecisionUnit)
#else
#define LINE_FRACTION 0.1
#endif
// intersect the end of the cubic with the other. Try lines from the end to control and opposite
// end to determine range of t on opposite cubic.
static bool intersectEnd(const Cubic& cubic1, bool start, const Cubic& cubic2, const _Rect& bounds2,
Intersections& i) {
// bool selfIntersect = cubic1 == cubic2;
_Line line;
int t1Index = start ? 0 : 3;
line[0] = cubic1[t1Index];
// don't bother if the two cubics are connnected
#if 0
if (!selfIntersect && (line[0].approximatelyEqual(cubic2[0])
|| line[0].approximatelyEqual(cubic2[3]))) {
return false;
}
#endif
bool result = false;
SkTDArray<double> tVals; // OPTIMIZE: replace with hard-sized array
for (int index = 0; index < 4; ++index) {
if (index == t1Index) {
continue;
}
_Vector dxy1 = cubic1[index] - line[0];
dxy1 /= gPrecisionUnit;
line[1] = line[0] + dxy1;
_Rect lineBounds;
lineBounds.setBounds(line);
if (!bounds2.intersects(lineBounds)) {
continue;
}
Intersections local;
if (!intersect(cubic2, line, local)) {
continue;
}
for (int idx2 = 0; idx2 < local.used(); ++idx2) {
double foundT = local.fT[0][idx2];
if (approximately_less_than_zero(foundT)
|| approximately_greater_than_one(foundT)) {
continue;
}
if (local.fPt[idx2].approximatelyEqual(line[0])) {
if (i.swapped()) { // FIXME: insert should respect swap
i.insert(foundT, start ? 0 : 1, line[0]);
} else {
i.insert(start ? 0 : 1, foundT, line[0]);
}
result = true;
} else {
*tVals.append() = local.fT[0][idx2];
}
}
}
if (tVals.count() == 0) {
return result;
}
QSort<double>(tVals.begin(), tVals.end() - 1);
double tMin1 = start ? 0 : 1 - LINE_FRACTION;
double tMax1 = start ? LINE_FRACTION : 1;
int tIdx = 0;
do {
int tLast = tIdx;
while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) {
++tLast;
}
double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
int lastUsed = i.used();
result |= intersect3(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i);
if (lastUsed == i.used()) {
tMin2 = SkTMax(tVals[tIdx] - (1.0 / gPrecisionUnit), 0.0);
tMax2 = SkTMin(tVals[tLast] + (1.0 / gPrecisionUnit), 1.0);
result |= intersect3(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i);
}
tIdx = tLast + 1;
} while (tIdx < tVals.count());
return result;
}
const double CLOSE_ENOUGH = 0.001;
static bool closeStart(const Cubic& cubic, int cubicIndex, Intersections& i, _Point& pt) {
if (i.fT[cubicIndex][0] != 0 || i.fT[cubicIndex][1] > CLOSE_ENOUGH) {
return false;
}
pt = xy_at_t(cubic, (i.fT[cubicIndex][0] + i.fT[cubicIndex][1]) / 2);
return true;
}
static bool closeEnd(const Cubic& cubic, int cubicIndex, Intersections& i, _Point& pt) {
int last = i.used() - 1;
if (i.fT[cubicIndex][last] != 1 || i.fT[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) {
return false;
}
pt = xy_at_t(cubic, (i.fT[cubicIndex][last] + i.fT[cubicIndex][last - 1]) / 2);
return true;
}
bool intersect3(const Cubic& c1, const Cubic& c2, Intersections& i) {
bool result = intersect3(c1, 0, 1, c2, 0, 1, 1, i);
// FIXME: pass in cached bounds from caller
_Rect c1Bounds, c2Bounds;
c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ?
c2Bounds.setBounds(c2);
result |= intersectEnd(c1, false, c2, c2Bounds, i);
result |= intersectEnd(c1, true, c2, c2Bounds, i);
bool selfIntersect = c1 == c2;
if (!selfIntersect) {
i.swap();
result |= intersectEnd(c2, false, c1, c1Bounds, i);
result |= intersectEnd(c2, true, c1, c1Bounds, i);
i.swap();
}
// If an end point and a second point very close to the end is returned, the second
// point may have been detected because the approximate quads
// intersected at the end and close to it. Verify that the second point is valid.
if (i.used() <= 1 || i.coincidentUsed()) {
return result;
}
_Point pt[2];
if (closeStart(c1, 0, i, pt[0]) && closeStart(c2, 1, i, pt[1])
&& pt[0].approximatelyEqual(pt[1])) {
i.removeOne(1);
}
if (closeEnd(c1, 0, i, pt[0]) && closeEnd(c2, 1, i, pt[1])
&& pt[0].approximatelyEqual(pt[1])) {
i.removeOne(i.used() - 2);
}
return result;
}
// Up promote the quad to a cubic.
// OPTIMIZATION If this is a common use case, optimize by duplicating
// the intersect 3 loop to avoid the promotion / demotion code
int intersect(const Cubic& cubic, const Quadratic& quad, Intersections& i) {
Cubic up;
toCubic(quad, up);
(void) intersect3(cubic, up, i);
return i.used();
}
/* http://www.ag.jku.at/compass/compasssample.pdf
( Self-Intersection Problems and Approximate Implicitization by Jan B. Thomassen
Centre of Mathematics for Applications, University of Oslo http://www.cma.uio.no janbth@math.uio.no
SINTEF Applied Mathematics http://www.sintef.no )
describes a method to find the self intersection of a cubic by taking the gradient of the implicit
form dotted with the normal, and solving for the roots. My math foo is too poor to implement this.*/
int intersect(const Cubic& c, Intersections& i) {
// check to see if x or y end points are the extrema. Are other quick rejects possible?
if (ends_are_extrema_in_x_or_y(c)) {
return false;
}
(void) intersect3(c, c, i);
if (i.used() > 0) {
SkASSERT(i.used() == 1);
if (i.fT[0][0] > i.fT[1][0]) {
SkTSwap(i.fT[0][0], i.fT[1][0]);
}
}
return i.used();
}

View File

@ -1,778 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "CubicIntersection_TestData.h"
#include "Intersection_Tests.h"
#include "Intersections.h"
#include "TestUtilities.h"
#define SHOW_ORIGINAL 1
const int firstCubicIntersectionTest = 9;
static void standardTestCases() {
for (size_t index = firstCubicIntersectionTest; index < tests_count; ++index) {
const Cubic& cubic1 = tests[index][0];
const Cubic& cubic2 = tests[index][1];
Cubic reduce1, reduce2;
int order1 = reduceOrder(cubic1, reduce1, kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_TreatAsFill);
int order2 = reduceOrder(cubic2, reduce2, kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order1 < 4) {
printf("%s [%d] cubic1 order=%d\n", __FUNCTION__, (int) index, order1);
continue;
}
if (order2 < 4) {
printf("%s [%d] cubic2 order=%d\n", __FUNCTION__, (int) index, order2);
continue;
}
if (implicit_matches(reduce1, reduce2)) {
printf("%s [%d] coincident\n", __FUNCTION__, (int) index);
continue;
}
Intersections tIntersections;
intersect(reduce1, reduce2, tIntersections);
if (!tIntersections.intersected()) {
printf("%s [%d] no intersection\n", __FUNCTION__, (int) index);
continue;
}
for (int pt = 0; pt < tIntersections.used(); ++pt) {
double tt1 = tIntersections.fT[0][pt];
double tx1, ty1;
xy_at_t(cubic1, tt1, tx1, ty1);
double tt2 = tIntersections.fT[1][pt];
double tx2, ty2;
xy_at_t(cubic2, tt2, tx2, ty2);
if (!AlmostEqualUlps(tx1, tx2)) {
printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
__FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
}
if (!AlmostEqualUlps(ty1, ty2)) {
printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
__FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
}
}
}
}
static const Cubic testSet[] = {
{{67.426548091427676, 37.993772624988935}, {23.483695892376684, 90.476863174921306}, {35.597065061143162, 79.872482633158796}, {75.38634169631932, 18.244890038969412}},
{{67.4265481, 37.9937726}, {23.4836959, 90.4768632}, {35.5970651, 79.8724826}, {75.3863417, 18.24489}},
{{0, 0}, {0, 1}, {1, 1}, {1, 0}},
{{1, 0}, {0, 0}, {0, 1}, {1, 1}},
{{0,1}, {4,5}, {1,0}, {5,3}},
{{0,1}, {3,5}, {1,0}, {5,4}},
{{0, 1}, {1, 6}, {1, 0}, {1, 0}},
{{0, 1}, {0, 1}, {1, 0}, {6, 1}},
{{0,1}, {3,4}, {1,0}, {5,1}},
{{0,1}, {1,5}, {1,0}, {4,3}},
{{0,1}, {1,2}, {1,0}, {6,1}},
{{0,1}, {1,6}, {1,0}, {2,1}},
{{0,1}, {0,5}, {1,0}, {4,0}},
{{0,1}, {0,4}, {1,0}, {5,0}},
{{0,1}, {3,4}, {1,0}, {3,0}},
{{0,1}, {0,3}, {1,0}, {4,3}},
{{0, 0}, {1, 2}, {3, 4}, {4, 4}},
{{0, 0}, {1, 2}, {3, 4}, {4, 4}},
{{4, 4}, {3, 4}, {1, 2}, {0, 0}},
{{0,1}, {2,3}, {1,0}, {1,0}},
{{0,1}, {0,1}, {1,0}, {3,2}},
{{0,2}, {0,1}, {1,0}, {1,0}},
{{0,1}, {0,1}, {2,0}, {1,0}},
{{0, 1}, {0, 2}, {1, 0}, {1, 0}},
{{0, 1}, {0, 1}, {1, 0}, {2, 0}},
{{0, 1}, {1, 6}, {1, 0}, {2, 0}},
{{0, 1}, {0, 2}, {1, 0}, {6, 1}},
{{0, 1}, {5, 6}, {1, 0}, {1, 0}},
{{0, 1}, {0, 1}, {1, 0}, {6, 5}},
{{95.837747722788592, 45.025976907939643}, {16.564570095652982, 0.72959763963222402}, {63.209855865319199, 68.047528419665767}, {57.640240647662544, 59.524565264361243}},
{{51.593891741518817, 38.53849970667553}, {62.34752929878772, 74.924924725166022}, {74.810149322641152, 34.17966562983564}, {29.368398119401373, 94.66719277886078}},
{{39.765160968417838, 33.060396198677083}, {5.1922921581157908, 66.854301452103215}, {31.619281802149157, 25.269248720849514}, {81.541621071073038, 70.025341524754353}},
{{46.078911165743556, 48.259962651999651}, {20.24450549867214, 49.403916182650214}, {0.26325131778756683, 24.46489805563581}, {15.915006546264051, 83.515023059917155}},
{{65.454505973241524, 93.881892270353575}, {45.867360264932437, 92.723972719499827}, {2.1464054482739447, 74.636369140183717}, {33.774068594804994, 40.770872887582925}},
{{72.963387832494163, 95.659300729473728}, {11.809496633619768, 82.209921247423594}, {13.456139067865974, 57.329313623406605}, {36.060621606214262, 70.867335643091849}},
{{32.484981432782945, 75.082940782924624}, {42.467313093350882, 48.131159948246157}, {3.5963115764764657, 43.208665839959245}, {79.442476890721579, 89.709102357602262}},
{{18.98573861410177, 93.308887208490106}, {40.405250173250792, 91.039661826118675}, {8.0467721950480584, 42.100282172719147}, {40.883324221187891, 26.030185504830527}},
{{7.5374809128872498, 82.441702896003477}, {22.444346930107265, 22.138854312775123}, {66.76091829629658, 50.753805856571446}, {78.193478508942519, 97.7932997968948}},
{{97.700573130371311, 53.53260215070685}, {87.72443481149358, 84.575876772671876}, {19.215031396232092, 47.032676472809484}, {11.989686410869325, 10.659507480757082}},
{{26.192053931854691, 9.8504326817814416}, {10.174241480498686, 98.476562741434464}, {21.177712558385782, 33.814968789841501}, {75.329030899018534, 55.02231980442177}},
{{56.222082700683771, 24.54395039218662}, {95.589995289030483, 81.050822735322086}, {28.180450866082897, 28.837706255185282}, {60.128952916771617, 87.311672180570511}},
{{42.449716172390481, 52.379709366885805}, {27.896043159019225, 48.797373636065686}, {92.770268299044233, 89.899302036454571}, {12.102066544863426, 99.43241951960718}},
{{45.77532924980639, 45.958701495993274}, {37.458701356062065, 68.393691335056758}, {37.569326692060258, 27.673713456687381}, {60.674866037757539, 62.47349659096146}},
{{67.426548091427676, 37.993772624988935}, {23.483695892376684, 90.476863174921306}, {35.597065061143162, 79.872482633158796}, {75.38634169631932, 18.244890038969412}},
{{61.336508189019057, 82.693132843213675}, {44.639380902349664, 54.074825790745592}, {16.815615499771951, 20.049704667203923}, {41.866884958868326, 56.735503699973002}},
{{67.4265481, 37.9937726}, {23.4836959, 90.4768632}, {35.5970651, 79.8724826}, {75.3863417, 18.24489}},
{{61.3365082, 82.6931328}, {44.6393809, 54.0748258}, {16.8156155, 20.0497047}, {41.866885, 56.7355037}},
{{18.1312339, 31.6473732}, {95.5711034, 63.5350219}, {92.3283165, 62.0158945}, {18.5656052, 32.1268808}},
{{97.402018, 35.7169972}, {33.1127443, 25.8935163}, {1.13970027, 54.9424981}, {56.4860195, 60.529264}},
};
const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
static const Cubic newTestSet[] = {
{{1,3}, {5,6}, {5,3}, {5,4}},
{{3,5}, {4,5}, {3,1}, {6,5}},
{{0,5}, {0,5}, {5,4}, {6,4}},
{{4,5}, {4,6}, {5,0}, {5,0}},
{{0,4}, {1,3}, {5,4}, {4,2}},
{{4,5}, {2,4}, {4,0}, {3,1}},
{{0,2}, {1,5}, {3,2}, {4,1}},
{{2,3}, {1,4}, {2,0}, {5,1}},
{{0,2}, {2,3}, {5,1}, {3,2}},
{{1,5}, {2,3}, {2,0}, {3,2}},
{{2,6}, {4,5}, {1,0}, {6,1}},
{{0,1}, {1,6}, {6,2}, {5,4}},
{{0,1}, {1,2}, {6,5}, {5,4}},
{{5,6}, {4,5}, {1,0}, {2,1}},
{{2.5119999999999996, 1.5710000000000002}, {2.6399999999999983, 1.6599999999999997}, {2.8000000000000007, 1.8000000000000003}, {3, 2}},
{{2.4181876227114887, 1.9849772580462195}, {2.8269904869227211, 2.009330650246834}, {3.2004679292461624, 1.9942047174679169}, {3.4986199496818058, 2.0035994597094731}},
{{2,3}, {1,4}, {1,0}, {6,0}},
{{0,1}, {0,6}, {3,2}, {4,1}},
{{0,2}, {1,5}, {1,0}, {6,1}},
{{0,1}, {1,6}, {2,0}, {5,1}},
{{0,1}, {1,5}, {2,1}, {4,0}},
{{1,2}, {0,4}, {1,0}, {5,1}},
{{0,1}, {3,5}, {2,1}, {3,1}},
{{1,2}, {1,3}, {1,0}, {5,3}},
{{0,1}, {2,5}, {6,0}, {5,3}},
{{0,6}, {3,5}, {1,0}, {5,2}},
{{0,1}, {3,6}, {1,0}, {5,2}},
{{0,1}, {2,5}, {1,0}, {6,3}},
{{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,3},{0,1},{2,0},{1,0}},
};
const size_t newTestSetCount = sizeof(newTestSet) / sizeof(newTestSet[0]);
#if 0
static void oneOff(const Cubic& cubic1, const Cubic& cubic2) {
SkTDArray<Quadratic> quads1;
cubic_to_quadratics(cubic1, calcPrecision(cubic1), quads1);
#if SHOW_ORIGINAL
SkDebugf("computed quadratics given\n");
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}, {%1.9g,%1.9g}},\n",
cubic1[0].x, cubic1[0].y, cubic1[1].x, cubic1[1].y,
cubic1[2].x, cubic1[2].y, cubic1[3].x, cubic1[3].y));
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}, {%1.9g,%1.9g}},\n",
cubic2[0].x, cubic2[0].y, cubic2[1].x, cubic2[1].y,
cubic2[2].x, cubic2[2].y, cubic2[3].x, cubic2[3].y));
#endif
#if ONE_OFF_DEBUG
SkDebugf("computed quadratics set 1\n");
for (int index = 0; index < quads1.count(); ++index) {
const Quadratic& q = quads1[index];
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].x, q[0].y,
q[1].x, q[1].y, q[2].x, q[2].y);
}
#endif
SkTDArray<Quadratic> quads2;
cubic_to_quadratics(cubic2, calcPrecision(cubic2), quads2);
#if ONE_OFF_DEBUG
SkDebugf("computed quadratics set 2\n");
for (int index = 0; index < quads2.count(); ++index) {
const Quadratic& q = quads2[index];
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].x, q[0].y,
q[1].x, q[1].y, q[2].x, q[2].y);
}
#endif
Intersections intersections2, intersections3;
intersect2(cubic1, cubic2, intersections2);
intersect3(cubic1, cubic2, intersections3);
int pt1, pt2, pt3;
bool found;
double tt1, tt2, last = -1;
_Point xy1, xy2;
for (pt1 = 0; pt1 < intersections2.used(); ++pt1) {
tt1 = intersections2.fT[0][pt1];
SkASSERT(!approximately_equal(last, tt1));
last = tt1;
xy_at_t(cubic1, tt1, xy1.x, xy1.y);
pt2 = intersections2.fFlip ? intersections2.used() - pt1 - 1 : pt1;
tt2 = intersections2.fT[1][pt2];
xy_at_t(cubic2, tt2, xy2.x, xy2.y);
#if ONE_OFF_DEBUG
SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n",
__FUNCTION__, tt1, xy1.x, xy1.y, intersections2.fPt[pt1].x,
intersections2.fPt[pt1].y, xy2.x, xy2.y, tt2);
#endif
SkASSERT(xy1.approximatelyEqual(xy2));
#ifdef SK_DEBUG
found = false;
for (pt3 = 0; pt3 < intersections3.used(); ++pt3) {
if (roughly_equal(tt1, intersections3.fT[0][pt3])) {
found = true;
break;
}
}
SkASSERT(found);
#endif
}
last = -1;
for (pt3 = 0; pt3 < intersections3.used(); ++pt3) {
found = false;
double tt3 = intersections3.fT[0][pt3];
SkASSERT(!approximately_equal(last, tt3));
last = tt3;
for (pt1 = 0; pt1 < intersections2.used(); ++pt1) {
if (approximately_equal(tt3, intersections2.fT[0][pt1])) {
found = true;
break;
}
}
if (!found) {
tt1 = intersections3.fT[0][pt3];
xy_at_t(cubic1, tt1, xy1.x, xy1.y);
pt2 = intersections3.fFlip ? intersections3.used() - pt3 - 1 : pt3;
tt2 = intersections3.fT[1][pt2];
xy_at_t(cubic2, tt2, xy2.x, xy2.y);
#if ONE_OFF_DEBUG
SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n",
__FUNCTION__, tt1, xy1.x, xy1.y, intersections3.fPt[pt1].x,
intersections3.fPt[pt1].y, xy2.x, xy2.y, tt2);
#endif
SkASSERT(xy1.approximatelyEqual(xy2));
SkDebugf("%s missing in intersect2\n", __FUNCTION__);
}
}
}
#endif
static void oneOff3(const Cubic& cubic1, const Cubic& cubic2) {
#if ONE_OFF_DEBUG
SkDebugf("computed quadratics given\n");
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
cubic1[0].x, cubic1[0].y, cubic1[1].x, cubic1[1].y,
cubic1[2].x, cubic1[2].y, cubic1[3].x, cubic1[3].y);
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
cubic2[0].x, cubic2[0].y, cubic2[1].x, cubic2[1].y,
cubic2[2].x, cubic2[2].y, cubic2[3].x, cubic2[3].y);
#endif
SkTDArray<Quadratic> quads1;
cubic_to_quadratics(cubic1, calcPrecision(cubic1), quads1);
#if ONE_OFF_DEBUG
SkDebugf("computed quadratics set 1\n");
for (int index = 0; index < quads1.count(); ++index) {
const Quadratic& q = quads1[index];
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].x, q[0].y,
q[1].x, q[1].y, q[2].x, q[2].y);
}
#endif
SkTDArray<Quadratic> quads2;
cubic_to_quadratics(cubic2, calcPrecision(cubic2), quads2);
#if ONE_OFF_DEBUG
SkDebugf("computed quadratics set 2\n");
for (int index = 0; index < quads2.count(); ++index) {
const Quadratic& q = quads2[index];
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].x, q[0].y,
q[1].x, q[1].y, q[2].x, q[2].y);
}
#endif
Intersections intersections3;
intersect3(cubic1, cubic2, intersections3);
int pt2, pt3;
double tt1, tt2, last = -1;
_Point xy1, xy2;
for (pt3 = 0; pt3 < intersections3.used(); ++pt3) {
double tt3 = intersections3.fT[0][pt3];
// SkASSERT(!approximately_equal(last, tt3));
last = tt3;
tt1 = intersections3.fT[0][pt3];
xy_at_t(cubic1, tt1, xy1.x, xy1.y);
pt2 = intersections3.fFlip ? intersections3.used() - pt3 - 1 : pt3;
tt2 = intersections3.fT[1][pt2];
xy_at_t(cubic2, tt2, xy2.x, xy2.y);
#if ONE_OFF_DEBUG
SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n",
__FUNCTION__, tt1, xy1.x, xy1.y, intersections3.fPt[pt3].x,
intersections3.fPt[pt3].y, xy2.x, xy2.y, tt2);
#endif
SkASSERT(xy1.approximatelyEqual(xy2));
}
}
#if 0
static int fails[][2] = { {0, 23}, // fails in intersect2 recursing
{2, 7}, // answers differ, but neither is correct ('3' is closer)
{3, 26}, // fails in intersect2 recursing
{4, 9}, // fails in intersect2 recursing
{4, 10}, // fails in intersect2 recursing
{10, 17}, // fails in intersect2 recursing
{12, 14}, // loops indefinitely
{12, 21}, // fails in intersect2 recursing
{13, 21}, // fails in intersect2 recursing
{14, 21}, // fails in intersect2 recursing
{17, 25}, // fails in intersect2 recursing
{23, 25}, // fails in intersect2 recursing
};
static int failCount = sizeof(fails) / sizeof(fails[0]);
#endif
static void oneOff(int outer, int inner) {
const Cubic& cubic1 = testSet[outer];
const Cubic& cubic2 = testSet[inner];
#if 0
bool failing = false;
for (int i = 0; i < failCount; ++i) {
if ((fails[i][0] == outer && fails[i][1] == inner)
|| (fails[i][1] == outer && fails[i][0] == inner)) {
failing = true;
break;
}
}
if (!failing) {
oneOff(cubic1, cubic2);
} else {
#endif
oneOff3(cubic1, cubic2);
// }
}
void CubicIntersection_OneOffTest() {
oneOff(0, 1);
}
static void newOneOff(int outer, int inner) {
const Cubic& cubic1 = newTestSet[outer];
const Cubic& cubic2 = newTestSet[inner];
oneOff3(cubic1, cubic2);
}
void CubicIntersection_NewOneOffTest() {
newOneOff(0, 1);
}
static void oneOffTests() {
for (size_t outer = 0; outer < testSetCount - 1; ++outer) {
for (size_t inner = outer + 1; inner < testSetCount; ++inner) {
oneOff(outer, inner);
}
}
}
void CubicIntersection_OneOffTests() {
oneOffTests();
}
#define DEBUG_CRASH 0
class CubicChopper {
public:
// only finds one intersection
CubicChopper(const Cubic& c1, const Cubic& c2)
: cubic1(c1)
, cubic2(c2)
, depth(0) {
}
bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
Cubic sub1, sub2;
// FIXME: carry last subdivide and reduceOrder result with cubic
sub_divide(cubic1, minT1, maxT1, sub1);
sub_divide(cubic2, minT2, maxT2, sub2);
Intersections i;
intersect3(sub1, sub2, i);
if (i.used() == 0) {
return false;
}
double x1, y1, x2, y2;
t1 = minT1 + i.fT[0][0] * (maxT1 - minT1);
t2 = minT2 + i.fT[1][0] * (maxT2 - minT2);
xy_at_t(cubic1, t1, x1, y1);
xy_at_t(cubic2, t2, x2, y2);
if (AlmostEqualUlps(x1, x2) && AlmostEqualUlps(y1, y2)) {
return true;
}
double half1 = (minT1 + maxT1) / 2;
double half2 = (minT2 + maxT2) / 2;
++depth;
bool result;
if (depth & 1) {
result = intersect(minT1, half1, minT2, maxT2) || intersect(half1, maxT1, minT2, maxT2)
|| intersect(minT1, maxT1, minT2, half2) || intersect(minT1, maxT1, half2, maxT2);
} else {
result = intersect(minT1, maxT1, minT2, half2) || intersect(minT1, maxT1, half2, maxT2)
|| intersect(minT1, half1, minT2, maxT2) || intersect(half1, maxT1, minT2, maxT2);
}
--depth;
return result;
}
const Cubic& cubic1;
const Cubic& cubic2;
double t1;
double t2;
int depth;
};
#define TRY_OLD 0 // old way fails on test == 1
void CubicIntersection_RandTestOld() {
srand(0);
const int tests = 1000000; // 10000000;
double largestFactor = DBL_MAX;
for (int test = 0; test < tests; ++test) {
Cubic cubic1, cubic2;
for (int i = 0; i < 4; ++i) {
cubic1[i].x = (double) rand() / RAND_MAX * 100;
cubic1[i].y = (double) rand() / RAND_MAX * 100;
cubic2[i].x = (double) rand() / RAND_MAX * 100;
cubic2[i].y = (double) rand() / RAND_MAX * 100;
}
if (test == 2513) { // the pair crosses three times, but the quadratic approximation
continue; // only sees one -- should be OK to ignore the other two?
}
if (test == 12932) { // this exposes a weakness when one cubic touches the other but
continue; // does not touch the quad approximation. Captured in qc.htm as cubic15
}
#if DEBUG_CRASH
char str[1024];
sprintf(str, "{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n"
"{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
cubic1[0].x, cubic1[0].y, cubic1[1].x, cubic1[1].y, cubic1[2].x, cubic1[2].y,
cubic1[3].x, cubic1[3].y,
cubic2[0].x, cubic2[0].y, cubic2[1].x, cubic2[1].y, cubic2[2].x, cubic2[2].y,
cubic2[3].x, cubic2[3].y);
#endif
_Rect rect1, rect2;
rect1.setBounds(cubic1);
rect2.setBounds(cubic2);
bool boundsIntersect = rect1.left <= rect2.right && rect2.left <= rect2.right
&& rect1.top <= rect2.bottom && rect2.top <= rect1.bottom;
Intersections i1, i2;
#if TRY_OLD
bool oldIntersects = intersect(cubic1, cubic2, i1);
#else
bool oldIntersects = false;
#endif
if (test == -1) {
SkDebugf("ready...\n");
}
bool newIntersects = intersect3(cubic1, cubic2, i2);
if (!boundsIntersect && (oldIntersects || newIntersects)) {
#if DEBUG_CRASH
SkDebugf("%s %d unexpected intersection boundsIntersect=%d oldIntersects=%d"
" newIntersects=%d\n%s %s\n", __FUNCTION__, test, boundsIntersect,
oldIntersects, newIntersects, __FUNCTION__, str);
#endif
SkASSERT(0);
}
if (oldIntersects && !newIntersects) {
#if DEBUG_CRASH
SkDebugf("%s %d missing intersection oldIntersects=%d newIntersects=%d\n%s %s\n",
__FUNCTION__, test, oldIntersects, newIntersects, __FUNCTION__, str);
#endif
SkASSERT(0);
}
if (!oldIntersects && !newIntersects) {
continue;
}
if (i2.used() > 1) {
continue;
// just look at single intercepts for simplicity
}
Intersections self1, self2; // self-intersect checks
if (intersect(cubic1, self1)) {
continue;
}
if (intersect(cubic2, self2)) {
continue;
}
// binary search for range necessary to enclose real intersection
CubicChopper c(cubic1, cubic2);
bool result = c.intersect(0, 1, 0, 1);
if (!result) {
// FIXME: a failure here probably means that a core routine used by CubicChopper is failing
continue;
}
double delta1 = fabs(c.t1 - i2.fT[0][0]);
double delta2 = fabs(c.t2 - i2.fT[1][0]);
double calc1 = calcPrecision(cubic1);
double calc2 = calcPrecision(cubic2);
double factor1 = calc1 / delta1;
double factor2 = calc2 / delta2;
SkDebugf("%s %d calc1=%1.9g delta1=%1.9g factor1=%1.9g calc2=%1.9g delta2=%1.9g"
" factor2=%1.9g\n", __FUNCTION__, test,
calc1, delta1, factor1, calc2, delta2, factor2);
if (factor1 < largestFactor) {
SkDebugf("WE HAVE A WINNER! %1.9g\n", factor1);
#if DEBUG_CRASH
SkDebugf("%s\n", str);
#endif
oneOff3(cubic1, cubic2);
largestFactor = factor1;
}
if (factor2 < largestFactor) {
SkDebugf("WE HAVE A WINNER! %1.9g\n", factor2);
#if DEBUG_CRASH
SkDebugf("%s\n", str);
#endif
oneOff3(cubic1, cubic2);
largestFactor = factor2;
}
}
}
void CubicIntersection_RandTest() {
srand(0);
const int tests = 10000000;
for (int test = 0; test < tests; ++test) {
Cubic cubic1, cubic2;
for (int i = 0; i < 4; ++i) {
cubic1[i].x = (double) rand() / RAND_MAX * 100;
cubic1[i].y = (double) rand() / RAND_MAX * 100;
cubic2[i].x = (double) rand() / RAND_MAX * 100;
cubic2[i].y = (double) rand() / RAND_MAX * 100;
}
#if DEBUG_CRASH
char str[1024];
sprintf(str, "{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n"
"{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
cubic1[0].x, cubic1[0].y, cubic1[1].x, cubic1[1].y, cubic1[2].x, cubic1[2].y,
cubic1[3].x, cubic1[3].y,
cubic2[0].x, cubic2[0].y, cubic2[1].x, cubic2[1].y, cubic2[2].x, cubic2[2].y,
cubic2[3].x, cubic2[3].y);
#endif
_Rect rect1, rect2;
rect1.setBounds(cubic1);
rect2.setBounds(cubic2);
bool boundsIntersect = rect1.left <= rect2.right && rect2.left <= rect2.right
&& rect1.top <= rect2.bottom && rect2.top <= rect1.bottom;
if (test == -1) {
SkDebugf("ready...\n");
}
Intersections intersections2;
bool newIntersects = intersect3(cubic1, cubic2, intersections2);
if (!boundsIntersect && newIntersects) {
#if DEBUG_CRASH
SkDebugf("%s %d unexpected intersection boundsIntersect=%d "
" newIntersects=%d\n%s %s\n", __FUNCTION__, test, boundsIntersect,
newIntersects, __FUNCTION__, str);
#endif
SkASSERT(0);
}
for (int pt = 0; pt < intersections2.used(); ++pt) {
double tt1 = intersections2.fT[0][pt];
_Point xy1, xy2;
xy_at_t(cubic1, tt1, xy1.x, xy1.y);
int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
double tt2 = intersections2.fT[1][pt2];
xy_at_t(cubic2, tt2, xy2.x, xy2.y);
#if 0
SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
tt1, xy1.x, xy1.y, xy2.x, xy2.y, tt2);
#endif
SkASSERT(xy1.approximatelyEqual(xy2));
}
}
}
static void intersectionFinder(int index0, int index1, double t1Seed, double t2Seed,
double t1Step, double t2Step) {
const Cubic& cubic1 = newTestSet[index0];
const Cubic& cubic2 = newTestSet[index1];
_Point t1[3], t2[3];
bool toggle = true;
do {
xy_at_t(cubic1, t1Seed - t1Step, t1[0].x, t1[0].y);
xy_at_t(cubic1, t1Seed, t1[1].x, t1[1].y);
xy_at_t(cubic1, t1Seed + t1Step, t1[2].x, t1[2].y);
xy_at_t(cubic2, t2Seed - t2Step, t2[0].x, t2[0].y);
xy_at_t(cubic2, t2Seed, t2[1].x, t2[1].y);
xy_at_t(cubic2, t2Seed + t2Step, t2[2].x, t2[2].y);
double dist[3][3];
dist[1][1] = t1[1].distance(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].distance(t2[j]);
if (dist[best_i][best_j] > dist[i][j]) {
best_i = i;
best_j = j;
}
}
}
if (best_i == 0) {
t1Seed -= t1Step;
} else if (best_i == 2) {
t1Seed += t1Step;
}
if (best_j == 0) {
t2Seed -= t2Step;
} else if (best_j == 2) {
t2Seed += t2Step;
}
if (best_i == 1 && best_j == 1) {
if ((toggle ^= true)) {
t1Step /= 2;
} else {
t2Step /= 2;
}
}
} while (!t1[1].approximatelyEqual(t2[1]));
t1Step = t2Step = 0.1;
double t10 = t1Seed - t1Step * 2;
double t12 = t1Seed + t1Step * 2;
double t20 = t2Seed - t2Step * 2;
double t22 = t2Seed + t2Step * 2;
_Point test;
while (!approximately_zero(t1Step)) {
xy_at_t(cubic1, t10, test.x, test.y);
t10 += t1[1].approximatelyEqual(test) ? -t1Step : t1Step;
t1Step /= 2;
}
t1Step = 0.1;
while (!approximately_zero(t1Step)) {
xy_at_t(cubic1, t12, test.x, test.y);
t12 -= t1[1].approximatelyEqual(test) ? -t1Step : t1Step;
t1Step /= 2;
}
while (!approximately_zero(t2Step)) {
xy_at_t(cubic2, t20, test.x, test.y);
t20 += t2[1].approximatelyEqual(test) ? -t2Step : t2Step;
t2Step /= 2;
}
t2Step = 0.1;
while (!approximately_zero(t2Step)) {
xy_at_t(cubic2, t22, test.x, test.y);
t22 -= t2[1].approximatelyEqual(test) ? -t2Step : t2Step;
t2Step /= 2;
}
#if ONE_OFF_DEBUG
SkDebugf("%s t1=(%1.9g<%1.9g<%1.9g) t2=(%1.9g<%1.9g<%1.9g)\n", __FUNCTION__,
t10, t1Seed, t12, t20, t2Seed, t22);
_Point p10 = xy_at_t(cubic1, t10);
_Point p1Seed = xy_at_t(cubic1, t1Seed);
_Point p12 = xy_at_t(cubic1, t12);
SkDebugf("%s p1=(%1.9g,%1.9g)<(%1.9g,%1.9g)<(%1.9g,%1.9g)\n", __FUNCTION__,
p10.x, p10.y, p1Seed.x, p1Seed.y, p12.x, p12.y);
_Point p20 = xy_at_t(cubic2, t20);
_Point p2Seed = xy_at_t(cubic2, t2Seed);
_Point p22 = xy_at_t(cubic2, t22);
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);
#endif
}
void CubicIntersection_IntersectionFinder() {
// double t1Seed = 0.87;
// double t2Seed = 0.87;
double t1Step = 0.000001;
double t2Step = 0.000001;
intersectionFinder(0, 1, 0.855895664, 0.864850875, t1Step, t2Step);
intersectionFinder(0, 1, 0.865207906, 0.865207887, t1Step, t2Step);
intersectionFinder(0, 1, 0.865213351, 0.865208087, t1Step, t2Step);
}
static void coincidentTest() {
#if 0
Cubic cubic1 = {{0, 1}, {0, 2}, {1, 0}, {1, 0}};
Cubic cubic2 = {{0, 1}, {0, 2}, {1, 0}, {6, 1}};
#endif
}
void CubicIntersection_SelfTest() {
const Cubic selfSet[] = {
{{0,2}, {2,3}, {5,1}, {3,2}},
{{0,2}, {3,5}, {5,0}, {4,2}},
{{3.34,8.98}, {1.95,10.27}, {3.76,7.65}, {4.96,10.64}},
{{3.13,2.74}, {1.08,4.62}, {3.71,0.94}, {2.01,3.81}},
{{6.71,3.14}, {7.99,2.75}, {8.27,1.96}, {6.35,3.57}},
{{12.81,7.27}, {7.22,6.98}, {12.49,8.97}, {11.42,6.18}},
};
size_t selfSetCount = sizeof(selfSet) / sizeof(selfSet[0]);
size_t firstFail = 1;
for (size_t index = firstFail; index < selfSetCount; ++index) {
const Cubic& cubic = selfSet[index];
#if ONE_OFF_DEBUG
int idx2;
double max[3];
int ts = find_cubic_max_curvature(cubic, max);
for (idx2 = 0; idx2 < ts; ++idx2) {
SkDebugf("%s max[%d]=%1.9g (%1.9g, %1.9g)\n", __FUNCTION__, idx2,
max[idx2], xy_at_t(cubic, max[idx2]).x, xy_at_t(cubic, max[idx2]).y);
}
SkTDArray<double> ts1;
SkTDArray<Quadratic> quads1;
cubic_to_quadratics(cubic, calcPrecision(cubic), ts1);
for (idx2 = 0; idx2 < ts1.count(); ++idx2) {
SkDebugf("%s t[%d]=%1.9g\n", __FUNCTION__, idx2, ts1[idx2]);
}
cubic_to_quadratics(cubic, calcPrecision(cubic), quads1);
for (idx2 = 0; idx2 < quads1.count(); ++idx2) {
const Quadratic& q = quads1[idx2];
SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
q[0].x, q[0].y, q[1].x, q[1].y, q[2].x, q[2].y);
}
SkDebugf("\n");
#endif
Intersections i;
SkDEBUGCODE(int result = ) intersect(cubic, i);
SkASSERT(result == 1);
SkASSERT(i.used() == 1);
SkASSERT(!approximately_equal(i.fT[0][0], i.fT[1][0]));
_Point pt1 = xy_at_t(cubic, i.fT[0][0]);
_Point pt2 = xy_at_t(cubic, i.fT[1][0]);
SkASSERT(pt1.approximatelyEqual(pt2));
}
}
void CubicIntersection_Test() {
oneOffTests();
coincidentTest();
standardTestCases();
}

View File

@ -1,310 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CubicIntersection_TestData.h"
#include <limits>
const Cubic pointDegenerates[] = {
{{0, 0}, {0, 0}, {0, 0}, {0, 0}},
{{1, 1}, {1, 1}, {1, 1}, {1, 1}},
{{1 + PointEpsilon - std::numeric_limits<double>::epsilon(), 1},
{1, 1 + PointEpsilon - std::numeric_limits<double>::epsilon()}, {1, 1}, {1, 1}},
{{1 + PointEpsilon/2 - std::numeric_limits<double>::epsilon(), 1},
{1 - (PointEpsilon/2 - std::numeric_limits<double>::epsilon()), 1}, {1, 1}, {1, 1}}
};
const size_t pointDegenerates_count = sizeof(pointDegenerates) / sizeof(pointDegenerates[0]);
const Cubic notPointDegenerates[] = {
{{1 + PointEpsilon + std::numeric_limits<double>::epsilon(), 1}, {1, 1 + PointEpsilon}, {1, 1}, {1, 1}},
{{1 + PointEpsilon/2 + std::numeric_limits<double>::epsilon(), 1}, {1 - PointEpsilon/2, 1}, {1, 1}, {1, 1}}
};
const size_t notPointDegenerates_count = sizeof(notPointDegenerates) / sizeof(notPointDegenerates[0]);
// from http://www.truetex.com/bezint.htm
const Cubic tests[][2] = {
{ // intersects in one place (data gives bezier clip fits
{{0, 45},
{6.0094158284751593, 51.610357411322688},
{12.741093228940867, 55.981703949474607},
{20.021417396476362, 58.652245509710262}},
{{2.2070737699246674, 52.703494107327209},
{31.591482272629477, 23.811002295222025},
{76.824588616426425, 44.049473790502674},
{119.25488947221436, 55.599248272955073}}
},
{ // intersects in three places
{{0, 45}, {50, 100}, {150, 0}, {200, 55}},
{{0, 55}, {50, 0}, {150, 100}, {200, 45}}
},
{ // intersects in one place, cross over is nearly parallel
{{0, 0}, {0, 100}, {200, 0}, {200, 100}},
{{0, 100}, {0, 0}, {200, 100}, {200, 0}}
},
{ // intersects in two places
{{0, 0}, {0, 100}, {200, 100}, {200, 0}},
{{0, 100}, {0, 0}, {200, 0}, {200, 100}}
},
{
{{150, 100}, {150 + 0.1, 150}, {150, 200}, {150, 250}},
{{250, 150}, {200, 150 + 0.1}, {150, 150}, {100, 150}}
},
{ // single intersection around 168,185
{{200, 100}, {150, 100}, {150, 150}, {200, 150}},
{{250, 150}, {250, 100}, {100, 100}, {100, 150}}
},
{
{{1.0, 1.5}, {15.5, 0.5}, {-8.0, 3.5}, {5.0, 1.5}},
{{4.0, 0.5}, {5.0, 15.0}, {2.0, -8.5}, {4.0, 4.5}}
},
{
{{664.00168, 0}, {726.11545, 124.22757}, {736.89069, 267.89743}, {694.0017, 400.0002}},
{{850.66843, 115.55563}, {728.515, 115.55563}, {725.21347, 275.15309}, {694.0017, 400.0002}}
},
{
{{1, 1}, {12.5, 6.5}, {-4, 6.5}, {7.5, 1}},
{{1, 6.5}, {12.5, 1}, {-4, 1}, {.5, 6}}
},
{
{{315.748, 312.84}, {312.644, 318.134}, {305.836, 319.909}, {300.542, 316.804}},
{{317.122, 309.05}, {316.112, 315.102}, {310.385, 319.19}, {304.332, 318.179}}
},
{
{{1046.604051, 172.937967}, {1046.604051, 178.9763059}, {1041.76745, 183.9279165}, {1035.703842, 184.0432409}},
{{1046.452235, 174.7640504}, {1045.544872, 180.1973817}, {1040.837966, 184.0469882}, {1035.505925, 184.0469882}}
},
{
{{125.79356, 199.57382}, {51.16556, 128.93575}, {87.494, 16.67848}, {167.29361, 16.67848}},
{{167.29361, 55.81876}, {100.36128, 55.81876}, {68.64099, 145.4755}, {125.7942, 199.57309}}
}
};
const size_t tests_count = sizeof(tests) / sizeof(tests[0]);
Cubic hexTests[][2] = {
{
{{0}} // placeholder for hex converted below
}
};
const size_t hexTests_count = sizeof(hexTests) / sizeof(hexTests[0]);
static const uint64_t testx[2][8] = {
{
0xf0d0d1ca63075a40LLU, 0x9408ce996a237740LLU, 0x6d5675460fbe5e40LLU, 0x6ef501e1b7487940LLU,
0x9a71d2f8143d6540LLU, 0x6bc18bbe02907a40LLU, 0x5b94d92093aa6b40LLU, 0x6ac18bbe02907a40LLU
},
{
0x92c56ed7b6145d40LLU, 0xede4f1255edb7740LLU, 0x1138c1101af75940LLU, 0x42e4f1255edb7740LLU,
0x408e51603ad95640LLU, 0x1e2e8fe9dd927740LLU, 0x1cb4777cd3a75440LLU, 0x212e1390de017740LLU
}
};
void convert_testx() {
const uint64_t* inPtr = testx[0];
double* outPtr = &hexTests[sizeof(tests) / sizeof(tests[0]) - 1][0][0].x;
for (unsigned index = 0; index < sizeof(testx) / sizeof(testx[0][0]); ++index) {
uint64_t input = *inPtr++;
unsigned char* output = (unsigned char*) outPtr++;
for (unsigned byte = 0; byte < sizeof(input); ++byte) {
output[byte] = input >> (7 - byte) * 8;
}
}
}
const Cubic lines[] = {
{{0, 0}, {0, 0}, {0, 0}, {1, 0}}, // 0: horizontal
{{0, 0}, {0, 0}, {1, 0}, {0, 0}},
{{0, 0}, {1, 0}, {0, 0}, {0, 0}},
{{1, 0}, {0, 0}, {0, 0}, {0, 0}},
{{1, 0}, {2, 0}, {3, 0}, {4, 0}},
{{0, 0}, {0, 0}, {0, 0}, {0, 1}}, // 5: vertical
{{0, 0}, {0, 0}, {0, 1}, {0, 0}},
{{0, 0}, {0, 1}, {0, 0}, {0, 0}},
{{0, 1}, {0, 0}, {0, 0}, {0, 0}},
{{0, 1}, {0, 2}, {0, 3}, {0, 4}},
{{0, 0}, {0, 0}, {0, 0}, {1, 1}}, // 10: 3 coincident
{{0, 0}, {0, 0}, {1, 1}, {0, 0}},
{{0, 0}, {1, 1}, {0, 0}, {0, 0}},
{{1, 1}, {0, 0}, {0, 0}, {0, 0}},
{{0, 0}, {0, 0}, {1, 1}, {2, 2}}, // 14: 2 coincident
{{0, 0}, {1, 1}, {0, 0}, {2, 2}},
{{0, 0}, {1, 1}, {2, 2}, {0, 0}},
{{1, 1}, {0, 0}, {0, 0}, {2, 2}}, // 17:
{{1, 1}, {0, 0}, {2, 2}, {0, 0}},
{{1, 1}, {2, 2}, {0, 0}, {0, 0}},
{{1, 1}, {2, 2}, {3, 3}, {2, 2}}, // middle-last coincident
{{1, 1}, {2, 2}, {3, 3}, {3, 3}}, // middle-last coincident
{{1, 1}, {1, 1}, {2, 2}, {2, 2}}, // 2 pairs coincident
{{1, 1}, {2, 2}, {1, 1}, {2, 2}},
{{1, 1}, {2, 2}, {2, 2}, {1, 1}},
{{1, 1}, {1, 1}, {3, 3}, {3, 3}}, // first-middle middle-last coincident
{{1, 1}, {2, 2}, {3, 3}, {4, 4}}, // no coincident
{{1, 1}, {3, 3}, {2, 2}, {4, 4}},
{{1, 1}, {2, 2}, {4, 4}, {3, 3}},
{{1, 1}, {3, 3}, {4, 4}, {2, 2}},
{{1, 1}, {4, 4}, {2, 2}, {3, 3}},
{{1, 1}, {4, 4}, {3, 3}, {2, 2}},
{{2, 2}, {1, 1}, {3, 3}, {4, 4}},
{{2, 2}, {1, 1}, {4, 4}, {3, 3}},
{{2, 2}, {3, 3}, {1, 1}, {4, 4}},
{{2, 2}, {3, 3}, {4, 4}, {1, 1}},
{{2, 2}, {4, 4}, {1, 1}, {3, 3}},
{{2, 2}, {4, 4}, {3, 3}, {1, 1}},
};
const size_t lines_count = sizeof(lines) / sizeof(lines[0]);
// 'not a line' tries to fool the line detection code
const Cubic notLines[] = {
{{0, 0}, {0, 0}, {0, 1}, {1, 0}},
{{0, 0}, {0, 1}, {0, 0}, {1, 0}},
{{0, 0}, {0, 1}, {1, 0}, {0, 0}},
{{0, 1}, {0, 0}, {0, 0}, {1, 0}},
{{0, 1}, {0, 0}, {1, 0}, {0, 0}},
{{0, 1}, {1, 0}, {0, 0}, {0, 0}},
};
const size_t notLines_count = sizeof(notLines) / sizeof(notLines[0]);
static const double E = PointEpsilon * 2;
static const double F = PointEpsilon * 3;
const Cubic modEpsilonLines[] = {
{{0, E}, {0, 0}, {0, 0}, {1, 0}}, // horizontal
{{0, 0}, {0, E}, {1, 0}, {0, 0}},
{{0, 0}, {1, 0}, {0, E}, {0, 0}},
{{1, 0}, {0, 0}, {0, 0}, {0, E}},
{{1, E}, {2, 0}, {3, 0}, {4, 0}},
{{E, 0}, {0, 0}, {0, 0}, {0, 1}}, // vertical
{{0, 0}, {E, 0}, {0, 1}, {0, 0}},
{{0, 0}, {0, 1}, {E, 0}, {0, 0}},
{{0, 1}, {0, 0}, {0, 0}, {E, 0}},
{{E, 1}, {0, 2}, {0, 3}, {0, 4}},
{{E, 0}, {0, 0}, {0, 0}, {1, 1}}, // 3 coincident
{{0, 0}, {E, 0}, {1, 1}, {0, 0}},
{{0, 0}, {1, 1}, {E, 0}, {0, 0}},
{{1, 1}, {0, 0}, {0, 0}, {E, 0}},
{{0, E}, {0, 0}, {1, 1}, {2, 2}}, // 2 coincident
{{0, 0}, {1, 1}, {0, E}, {2, 2}},
{{0, 0}, {1, 1}, {2, 2}, {0, E}},
{{1, 1}, {0, E}, {0, 0}, {2, 2}},
{{1, 1}, {0, E}, {2, 2}, {0, 0}},
{{1, 1}, {2, 2}, {E, 0}, {0, 0}},
{{1, 1}, {2, 2+E}, {3, 3}, {2, 2}}, // middle-last coincident
{{1, 1}, {2+E, 2}, {3, 3}, {3, 3}}, // middle-last coincident
{{1, 1}, {1, 1}, {2, 2}, {2+E, 2}}, // 2 pairs coincident
{{1, 1}, {2, 2}, {1, 1}, {2+E, 2}},
{{1, 1}, {2, 2}, {2, 2+E}, {1, 1}},
{{1, 1}, {1, 1+E}, {3, 3}, {3, 3}}, // first-middle middle-last coincident
{{1, 1}, {2+E, 2}, {3, 3}, {4, 4}}, // no coincident
{{1, 1}, {3, 3}, {2, 2}, {4, 4+F}}, // INVESTIGATE: why the epsilon is bigger
{{1, 1+F}, {2, 2}, {4, 4}, {3, 3}}, // INVESTIGATE: why the epsilon is bigger
{{1, 1}, {3, 3}, {4, 4+E}, {2, 2}},
{{1, 1}, {4, 4}, {2, 2}, {3, 3+E}},
{{1, 1}, {4, 4}, {3, 3}, {2+E, 2}},
{{2, 2}, {1, 1}, {3+E, 3}, {4, 4}},
{{2, 2}, {1+E, 1}, {4, 4}, {3, 3}},
{{2, 2+E}, {3, 3}, {1, 1}, {4, 4}},
{{2+E, 2}, {3, 3}, {4, 4}, {1, 1}},
{{2, 2}, {4+E, 4}, {1, 1}, {3, 3}},
{{2, 2}, {4, 4}, {3, 3}, {1, 1+E}},
};
const size_t modEpsilonLines_count = sizeof(modEpsilonLines) / sizeof(modEpsilonLines[0]);
static const double D = PointEpsilon / 2;
static const double G = PointEpsilon / 3;
const Cubic lessEpsilonLines[] = {
{{0, D}, {0, 0}, {0, 0}, {1, 0}}, // horizontal
{{0, 0}, {0, D}, {1, 0}, {0, 0}},
{{0, 0}, {1, 0}, {0, D}, {0, 0}},
{{1, 0}, {0, 0}, {0, 0}, {0, D}},
{{1, D}, {2, 0}, {3, 0}, {4, 0}},
{{D, 0}, {0, 0}, {0, 0}, {0, 1}}, // vertical
{{0, 0}, {D, 0}, {0, 1}, {0, 0}},
{{0, 0}, {0, 1}, {D, 0}, {0, 0}},
{{0, 1}, {0, 0}, {0, 0}, {D, 0}},
{{D, 1}, {0, 2}, {0, 3}, {0, 4}},
{{D, 0}, {0, 0}, {0, 0}, {1, 1}}, // 3 coincident
{{0, 0}, {D, 0}, {1, 1}, {0, 0}},
{{0, 0}, {1, 1}, {D, 0}, {0, 0}},
{{1, 1}, {0, 0}, {0, 0}, {D, 0}},
{{0, D}, {0, 0}, {1, 1}, {2, 2}}, // 2 coincident
{{0, 0}, {1, 1}, {0, D}, {2, 2}},
{{0, 0}, {1, 1}, {2, 2}, {0, D}},
{{1, 1}, {0, D}, {0, 0}, {2, 2}},
{{1, 1}, {0, D}, {2, 2}, {0, 0}},
{{1, 1}, {2, 2}, {D, 0}, {0, 0}},
{{1, 1}, {2, 2+D}, {3, 3}, {2, 2}}, // middle-last coincident
{{1, 1}, {2+D, 2}, {3, 3}, {3, 3}}, // middle-last coincident
{{1, 1}, {1, 1}, {2, 2}, {2+D, 2}}, // 2 pairs coincident
{{1, 1}, {2, 2}, {1, 1}, {2+D, 2}},
{{1, 1}, {2, 2}, {2, 2+D}, {1, 1}},
{{1, 1}, {1, 1+D}, {3, 3}, {3, 3}}, // first-middle middle-last coincident
{{1, 1}, {2+D/2, 2}, {3, 3}, {4, 4}}, // no coincident (FIXME: N as opposed to N/2 failed)
{{1, 1}, {3, 3}, {2, 2}, {4, 4+D}},
{{1, 1+D}, {2, 2}, {4, 4}, {3, 3}},
{{1, 1}, {3, 3}, {4, 4+D}, {2, 2}},
{{1, 1}, {4, 4}, {2, 2}, {3, 3+D}},
{{1, 1}, {4, 4}, {3, 3}, {2+G, 2}}, // INVESTIGATE: why the epsilon is smaller
{{2, 2}, {1, 1}, {3+D, 3}, {4, 4}},
{{2, 2}, {1+D, 1}, {4, 4}, {3, 3}},
{{2, 2+D}, {3, 3}, {1, 1}, {4, 4}},
{{2+G, 2}, {3, 3}, {4, 4}, {1, 1}}, // INVESTIGATE: why the epsilon is smaller
{{2, 2}, {4+D, 4}, {1, 1}, {3, 3}},
{{2, 2}, {4, 4}, {3, 3}, {1, 1+D}},
};
const size_t lessEpsilonLines_count = sizeof(lessEpsilonLines) / sizeof(lessEpsilonLines[0]);
static const double N = -PointEpsilon / 2;
static const double M = -PointEpsilon / 3;
const Cubic negEpsilonLines[] = {
{{0, N}, {0, 0}, {0, 0}, {1, 0}}, // horizontal
{{0, 0}, {0, N}, {1, 0}, {0, 0}},
{{0, 0}, {1, 0}, {0, N}, {0, 0}},
{{1, 0}, {0, 0}, {0, 0}, {0, N}},
{{1, N}, {2, 0}, {3, 0}, {4, 0}},
{{N, 0}, {0, 0}, {0, 0}, {0, 1}}, // vertical
{{0, 0}, {N, 0}, {0, 1}, {0, 0}},
{{0, 0}, {0, 1}, {N, 0}, {0, 0}},
{{0, 1}, {0, 0}, {0, 0}, {N, 0}},
{{N, 1}, {0, 2}, {0, 3}, {0, 4}},
{{N, 0}, {0, 0}, {0, 0}, {1, 1}}, // 3 coincident
{{0, 0}, {N, 0}, {1, 1}, {0, 0}},
{{0, 0}, {1, 1}, {N, 0}, {0, 0}},
{{1, 1}, {0, 0}, {0, 0}, {N, 0}},
{{0, N}, {0, 0}, {1, 1}, {2, 2}}, // 2 coincident
{{0, 0}, {1, 1}, {0, N}, {2, 2}},
{{0, 0}, {1, 1}, {2, 2}, {0, N}},
{{1, 1}, {0, N}, {0, 0}, {2, 2}},
{{1, 1}, {0, N}, {2, 2}, {0, 0}},
{{1, 1}, {2, 2}, {N, 0}, {0, 0}},
{{1, 1}, {2, 2+N}, {3, 3}, {2, 2}}, // middle-last coincident
{{1, 1}, {2+N, 2}, {3, 3}, {3, 3}}, // middle-last coincident
{{1, 1}, {1, 1}, {2, 2}, {2+N, 2}}, // 2 pairs coincident
{{1, 1}, {2, 2}, {1, 1}, {2+N, 2}},
{{1, 1}, {2, 2}, {2, 2+N}, {1, 1}},
{{1, 1}, {1, 1+N}, {3, 3}, {3, 3}}, // first-middle middle-last coincident
{{1, 1}, {2+N/2, 2}, {3, 3}, {4, 4}}, // no coincident (FIXME: N as opposed to N/2 failed)
{{1, 1}, {3, 3}, {2, 2}, {4, 4+N}},
{{1, 1+N}, {2, 2}, {4, 4}, {3, 3}},
{{1, 1}, {3, 3}, {4, 4+N}, {2, 2}},
{{1, 1}, {4, 4}, {2, 2}, {3, 3+N}},
{{1, 1}, {4, 4}, {3, 3}, {2+M, 2}}, // INVESTIGATE: why the epsilon is smaller
{{2, 2}, {1, 1}, {3+N, 3}, {4, 4}},
{{2, 2}, {1+N, 1}, {4, 4}, {3, 3}},
{{2, 2+N}, {3, 3}, {1, 1}, {4, 4}},
{{2+M, 2}, {3, 3}, {4, 4}, {1, 1}}, // INVESTIGATE: why the epsilon is smaller
{{2, 2}, {4+N, 4}, {1, 1}, {3, 3}},
{{2, 2}, {4, 4}, {3, 3}, {1, 1+N}},
};
const size_t negEpsilonLines_count = sizeof(negEpsilonLines) / sizeof(negEpsilonLines[0]);

View File

@ -1,31 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
#include "DataTypes_Test.h"
extern const Cubic pointDegenerates[];
extern const Cubic notPointDegenerates[];
extern const Cubic tests[][2];
extern Cubic hexTests[][2];
extern void convert_testx();
extern const Cubic lines[];
extern const Cubic notLines[];
extern const Cubic modEpsilonLines[];
extern const Cubic lessEpsilonLines[];
extern const Cubic negEpsilonLines[];
extern const size_t pointDegenerates_count;
extern const size_t notPointDegenerates_count;
extern const size_t tests_count;
extern const size_t hexTests_count;
extern const size_t lines_count;
extern const size_t notLines_count;
extern const size_t modEpsilonLines_count;
extern const size_t lessEpsilonLines_count;
extern const size_t negEpsilonLines_count;

View File

@ -1,38 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CubicLineSegments.h"
#include "QuadraticLineSegments.h"
// http://cagd.cs.byu.edu/~557/text/cagd.pdf 2.7
// A hodograph is the first derivative curve
void hodograph(const Cubic& cubic, Quadratic& hodo) {
hodo[0].x = 3 * (cubic[1].x - cubic[0].x);
hodo[0].y = 3 * (cubic[1].y - cubic[0].y);
hodo[1].x = 3 * (cubic[2].x - cubic[1].x);
hodo[1].y = 3 * (cubic[2].y - cubic[1].y);
hodo[2].x = 3 * (cubic[3].x - cubic[2].x);
hodo[2].y = 3 * (cubic[3].y - cubic[2].y);
}
// A 2nd hodograph is the second derivative curve
void secondHodograph(const Cubic& cubic, _Line& hodo2) {
Quadratic hodo;
hodograph(cubic, hodo);
hodograph(hodo, hodo2);
}
// The number of line segments required to approximate the cubic
// see http://cagd.cs.byu.edu/~557/text/cagd.pdf 10.6
double subDivisions(const Cubic& cubic) {
_Line hodo2;
secondHodograph(cubic, hodo2);
double maxX = SkTMax(hodo2[1].x, hodo2[1].x);
double maxY = SkTMax(hodo2[1].y, hodo2[1].y);
double dist = sqrt(maxX * maxX + maxY * maxY);
double segments = sqrt(dist / (8 * FLT_EPSILON));
return segments;
}

View File

@ -1,12 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
void hodograph(const Cubic& , Quadratic& hodo);
void secondHodograph(const Cubic& , _Line& hodo2);
double subDivisions(const Cubic& );

View File

@ -1,518 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CubicUtilities.h"
/* from http://tom.cs.byu.edu/~tom/papers/cvgip84.pdf 4.1
*
* This paper proves that Syvester's method can compute the implicit form of
* the quadratic from the parameterzied form.
*
* Given x = a*t*t*t + b*t*t + c*t + d (the parameterized form)
* y = e*t*t*t + f*t*t + g*t + h
*
* we want to find an equation of the implicit form:
*
* A*x^3 + B*x*x*y + C*x*y*y + D*y^3 + E*x*x + F*x*y + G*y*y + H*x + I*y + J = 0
*
* The implicit form can be expressed as a 6x6 determinant, as shown.
*
* The resultant obtained by Syvester's method is
*
* | a b c (d - x) 0 0 |
* | 0 a b c (d - x) 0 |
* | 0 0 a b c (d - x) |
* | e f g (h - y) 0 0 |
* | 0 e f g (h - y) 0 |
* | 0 0 e f g (h - y) |
*
* which, according to Mathematica, expands as shown below.
*
* Resultant[a*t^3 + b*t^2 + c*t + d - x, e*t^3 + f*t^2 + g*t + h - y, t]
*
* -d^3 e^3 + c d^2 e^2 f - b d^2 e f^2 + a d^2 f^3 - c^2 d e^2 g +
* 2 b d^2 e^2 g + b c d e f g - 3 a d^2 e f g - a c d f^2 g -
* b^2 d e g^2 + 2 a c d e g^2 + a b d f g^2 - a^2 d g^3 + c^3 e^2 h -
* 3 b c d e^2 h + 3 a d^2 e^2 h - b c^2 e f h + 2 b^2 d e f h +
* a c d e f h + a c^2 f^2 h - 2 a b d f^2 h + b^2 c e g h -
* 2 a c^2 e g h - a b d e g h - a b c f g h + 3 a^2 d f g h +
* a^2 c g^2 h - b^3 e h^2 + 3 a b c e h^2 - 3 a^2 d e h^2 +
* a b^2 f h^2 - 2 a^2 c f h^2 - a^2 b g h^2 + a^3 h^3 + 3 d^2 e^3 x -
* 2 c d e^2 f x + 2 b d e f^2 x - 2 a d f^3 x + c^2 e^2 g x -
* 4 b d e^2 g x - b c e f g x + 6 a d e f g x + a c f^2 g x +
* b^2 e g^2 x - 2 a c e g^2 x - a b f g^2 x + a^2 g^3 x +
* 3 b c e^2 h x - 6 a d e^2 h x - 2 b^2 e f h x - a c e f h x +
* 2 a b f^2 h x + a b e g h x - 3 a^2 f g h x + 3 a^2 e h^2 x -
* 3 d e^3 x^2 + c e^2 f x^2 - b e f^2 x^2 + a f^3 x^2 +
* 2 b e^2 g x^2 - 3 a e f g x^2 + 3 a e^2 h x^2 + e^3 x^3 -
* c^3 e^2 y + 3 b c d e^2 y - 3 a d^2 e^2 y + b c^2 e f y -
* 2 b^2 d e f y - a c d e f y - a c^2 f^2 y + 2 a b d f^2 y -
* b^2 c e g y + 2 a c^2 e g y + a b d e g y + a b c f g y -
* 3 a^2 d f g y - a^2 c g^2 y + 2 b^3 e h y - 6 a b c e h y +
* 6 a^2 d e h y - 2 a b^2 f h y + 4 a^2 c f h y + 2 a^2 b g h y -
* 3 a^3 h^2 y - 3 b c e^2 x y + 6 a d e^2 x y + 2 b^2 e f x y +
* a c e f x y - 2 a b f^2 x y - a b e g x y + 3 a^2 f g x y -
* 6 a^2 e h x y - 3 a e^2 x^2 y - b^3 e y^2 + 3 a b c e y^2 -
* 3 a^2 d e y^2 + a b^2 f y^2 - 2 a^2 c f y^2 - a^2 b g y^2 +
* 3 a^3 h y^2 + 3 a^2 e x y^2 - a^3 y^3
*/
enum {
xxx_coeff, // A
xxy_coeff, // B
xyy_coeff, // C
yyy_coeff, // D
xx_coeff,
xy_coeff,
yy_coeff,
x_coeff,
y_coeff,
c_coeff,
coeff_count
};
#define USE_SYVESTER 0 // if 0, use control-point base parametric form
#if USE_SYVESTER
// FIXME: factoring version unwritten
// static bool straight_forward = true;
/* from CubicParameterizationCode.cpp output:
* double A = e * e * e;
* double B = -3 * a * e * e;
* double C = 3 * a * a * e;
* double D = -a * a * a;
*/
static void calc_ABCD(double a, double e, double p[coeff_count]) {
double ee = e * e;
p[xxx_coeff] = e * ee;
p[xxy_coeff] = -3 * a * ee;
double aa = a * a;
p[xyy_coeff] = 3 * aa * e;
p[yyy_coeff] = -aa * a;
}
/* CubicParameterizationCode.cpp turns Mathematica output into C.
* Rather than edit the lines below, please edit the code there instead.
*/
// start of generated code
static double calc_xx(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
-3 * d * e * e * e
+ c * e * e * f
- b * e * f * f
+ a * f * f * f
+ 2 * b * e * e * g
- 3 * a * e * f * g
+ 3 * a * e * e * h;
}
static double calc_xy(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
-3 * b * c * e * e
+ 6 * a * d * e * e
+ 2 * b * b * e * f
+ a * c * e * f
- 2 * a * b * f * f
- a * b * e * g
+ 3 * a * a * f * g
- 6 * a * a * e * h;
}
static double calc_yy(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
-b * b * b * e
+ 3 * a * b * c * e
- 3 * a * a * d * e
+ a * b * b * f
- 2 * a * a * c * f
- a * a * b * g
+ 3 * a * a * a * h;
}
static double calc_x(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
3 * d * d * e * e * e
- 2 * c * d * e * e * f
+ 2 * b * d * e * f * f
- 2 * a * d * f * f * f
+ c * c * e * e * g
- 4 * b * d * e * e * g
- b * c * e * f * g
+ 6 * a * d * e * f * g
+ a * c * f * f * g
+ b * b * e * g * g
- 2 * a * c * e * g * g
- a * b * f * g * g
+ a * a * g * g * g
+ 3 * b * c * e * e * h
- 6 * a * d * e * e * h
- 2 * b * b * e * f * h
- a * c * e * f * h
+ 2 * a * b * f * f * h
+ a * b * e * g * h
- 3 * a * a * f * g * h
+ 3 * a * a * e * h * h;
}
static double calc_y(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
-c * c * c * e * e
+ 3 * b * c * d * e * e
- 3 * a * d * d * e * e
+ b * c * c * e * f
- 2 * b * b * d * e * f
- a * c * d * e * f
- a * c * c * f * f
+ 2 * a * b * d * f * f
- b * b * c * e * g
+ 2 * a * c * c * e * g
+ a * b * d * e * g
+ a * b * c * f * g
- 3 * a * a * d * f * g
- a * a * c * g * g
+ 2 * b * b * b * e * h
- 6 * a * b * c * e * h
+ 6 * a * a * d * e * h
- 2 * a * b * b * f * h
+ 4 * a * a * c * f * h
+ 2 * a * a * b * g * h
- 3 * a * a * a * h * h;
}
static double calc_c(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
-d * d * d * e * e * e
+ c * d * d * e * e * f
- b * d * d * e * f * f
+ a * d * d * f * f * f
- c * c * d * e * e * g
+ 2 * b * d * d * e * e * g
+ b * c * d * e * f * g
- 3 * a * d * d * e * f * g
- a * c * d * f * f * g
- b * b * d * e * g * g
+ 2 * a * c * d * e * g * g
+ a * b * d * f * g * g
- a * a * d * g * g * g
+ c * c * c * e * e * h
- 3 * b * c * d * e * e * h
+ 3 * a * d * d * e * e * h
- b * c * c * e * f * h
+ 2 * b * b * d * e * f * h
+ a * c * d * e * f * h
+ a * c * c * f * f * h
- 2 * a * b * d * f * f * h
+ b * b * c * e * g * h
- 2 * a * c * c * e * g * h
- a * b * d * e * g * h
- a * b * c * f * g * h
+ 3 * a * a * d * f * g * h
+ a * a * c * g * g * h
- b * b * b * e * h * h
+ 3 * a * b * c * e * h * h
- 3 * a * a * d * e * h * h
+ a * b * b * f * h * h
- 2 * a * a * c * f * h * h
- a * a * b * g * h * h
+ a * a * a * h * h * h;
}
// end of generated code
#else
/* more Mathematica generated code. This takes a different tack, starting with
the control-point based parametric formulas. The C code is unoptimized --
in this form, this is a proof of concept (since the other code didn't work)
*/
static double calc_c(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
d*d*d*e*e*e - 3*d*d*(3*c*e*e*f + 3*b*e*(-3*f*f + 2*e*g) + a*(9*f*f*f - 9*e*f*g + e*e*h)) -
h*(27*c*c*c*e*e - 27*c*c*(3*b*e*f - 3*a*f*f + 2*a*e*g) +
h*(-27*b*b*b*e + 27*a*b*b*f - 9*a*a*b*g + a*a*a*h) +
9*c*(9*b*b*e*g + a*b*(-9*f*g + 3*e*h) + a*a*(3*g*g - 2*f*h))) +
3*d*(9*c*c*e*e*g + 9*b*b*e*(3*g*g - 2*f*h) + 3*a*b*(-9*f*g*g + 6*f*f*h + e*g*h) +
a*a*(9*g*g*g - 9*f*g*h + e*h*h) + 3*c*(3*b*e*(-3*f*g + e*h) + a*(9*f*f*g - 6*e*g*g - e*f*h)))
;
}
// - Power(e - 3*f + 3*g - h,3)*Power(x,3)
static double calc_xxx(double e3f3gh) {
return -e3f3gh * e3f3gh * e3f3gh;
}
static double calc_y(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
+ 3*(6*b*d*d*e*e - d*d*d*e*e + 18*b*b*d*e*f - 18*b*d*d*e*f -
9*b*d*d*f*f - 54*b*b*d*e*g + 12*b*d*d*e*g - 27*b*b*d*g*g - 18*b*b*b*e*h + 18*b*b*d*e*h +
18*b*b*d*f*h + a*a*a*h*h - 9*b*b*b*h*h + 9*c*c*c*e*(e + 2*h) +
a*a*(-3*b*h*(2*g + h) + d*(-27*g*g + 9*g*h - h*(2*e + h) + 9*f*(g + h))) +
a*(9*b*b*h*(2*f + h) - 3*b*d*(6*f*f - 6*f*(3*g - 2*h) + g*(-9*g + h) + e*(g + h)) +
d*d*(e*e + 9*f*(3*f - g) + e*(-9*f - 9*g + 2*h))) -
9*c*c*(d*e*(e + 2*g) + 3*b*(f*h + e*(f + h)) + a*(-3*f*f - 6*f*h + 2*(g*h + e*(g + h)))) +
3*c*(d*d*e*(e + 2*f) + a*a*(3*g*g + 6*g*h - 2*h*(2*f + h)) + 9*b*b*(g*h + e*(g + h)) +
a*d*(-9*f*f - 18*f*g + 6*g*g + f*h + e*(f + 12*g + h)) +
b*(d*(-3*e*e + 9*f*g + e*(9*f + 9*g - 6*h)) + 3*a*(h*(2*e - 3*g + h) - 3*f*(g + h))))) // *y
;
}
static double calc_yy(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
- 3*(18*c*c*c*e - 18*c*c*d*e + 6*c*d*d*e - d*d*d*e + 3*c*d*d*f - 9*c*c*d*g + a*a*a*h + 9*c*c*c*h -
9*b*b*b*(e + 2*h) - a*a*(d*(e - 9*f + 18*g - 7*h) + 3*c*(2*f - 6*g + h)) +
a*(-9*c*c*(2*e - 6*f + 2*g - h) + d*d*(-7*e + 18*f - 9*g + h) + 3*c*d*(7*e - 17*f + 3*g + h)) +
9*b*b*(3*c*(e + g + h) + a*(f + 2*h) - d*(e - 2*(f - 3*g + h))) -
3*b*(-(d*d*(e - 6*f + 2*g)) - 3*c*d*(e + 3*f + 3*g - h) + 9*c*c*(e + f + h) + a*a*(g + 2*h) +
a*(c*(-3*e + 9*f + 9*g + 3*h) + d*(e + 3*f - 17*g + 7*h)))) // *Power(y,2)
;
}
// + Power(a - 3*b + 3*c - d,3)*Power(y,3)
static double calc_yyy(double a3b3cd) {
return a3b3cd * a3b3cd * a3b3cd;
}
static double calc_xx(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
// + Power(x,2)*
(-3*(-9*b*e*f*f + 9*a*f*f*f + 6*b*e*e*g - 9*a*e*f*g + 27*b*e*f*g - 27*a*f*f*g + 18*a*e*g*g - 54*b*e*g*g +
27*a*f*g*g + 27*b*f*g*g - 18*a*g*g*g + a*e*e*h - 9*b*e*e*h + 3*a*e*f*h + 9*b*e*f*h + 9*a*f*f*h -
18*b*f*f*h - 21*a*e*g*h + 51*b*e*g*h - 9*a*f*g*h - 27*b*f*g*h + 18*a*g*g*h + 7*a*e*h*h - 18*b*e*h*h - 3*a*f*h*h +
18*b*f*h*h - 6*a*g*h*h - 3*b*g*h*h + a*h*h*h +
3*c*(-9*f*f*(g - 2*h) + 3*g*g*h - f*h*(9*g + 2*h) + e*e*(f - 6*g + 6*h) +
e*(9*f*g + 6*g*g - 17*f*h - 3*g*h + 3*h*h)) -
d*(e*e*e + e*e*(-6*f - 3*g + 7*h) - 9*(2*f - g)*(f*f + g*g - f*(g + h)) +
e*(18*f*f + 9*g*g + 3*g*h + h*h - 3*f*(3*g + 7*h)))) )
;
}
// + Power(x,2)*(3*(a - 3*b + 3*c - d)*Power(e - 3*f + 3*g - h,2)*y)
static double calc_xxy(double a3b3cd, double e3f3gh) {
return 3 * a3b3cd * e3f3gh * e3f3gh;
}
static double calc_x(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
// + x*
(-3*(27*b*b*e*g*g - 27*a*b*f*g*g + 9*a*a*g*g*g - 18*b*b*e*f*h + 18*a*b*f*f*h + 3*a*b*e*g*h -
27*b*b*e*g*h - 9*a*a*f*g*h + 27*a*b*f*g*h - 9*a*a*g*g*h + a*a*e*h*h - 9*a*b*e*h*h +
27*b*b*e*h*h + 6*a*a*f*h*h - 18*a*b*f*h*h - 9*b*b*f*h*h + 3*a*a*g*h*h +
6*a*b*g*h*h - a*a*h*h*h + 9*c*c*(e*e*(g - 3*h) - 3*f*f*h + e*(3*f + 2*g)*h) +
d*d*(e*e*e - 9*f*f*f + 9*e*f*(f + g) - e*e*(3*f + 6*g + h)) +
d*(-3*c*(-9*f*f*g + e*e*(2*f - 6*g - 3*h) + e*(9*f*g + 6*g*g + f*h)) +
a*(-18*f*f*f - 18*e*g*g + 18*g*g*g - 2*e*e*h + 3*e*g*h + 2*e*h*h + 9*f*f*(3*g + 2*h) +
3*f*(6*e*g - 9*g*g - e*h - 6*g*h)) - 3*b*(9*f*g*g + e*e*(4*g - 3*h) - 6*f*f*h -
e*(6*f*f + g*(18*g + h) - 3*f*(3*g + 4*h)))) +
3*c*(3*b*(e*e*h + 3*f*g*h - e*(3*f*g - 6*f*h + 6*g*h + h*h)) +
a*(9*f*f*(g - 2*h) + f*h*(-e + 9*g + 4*h) - 3*(2*g*g*h + e*(2*g*g - 4*g*h + h*h))))) )
;
}
static double calc_xy(double a, double b, double c, double d,
double e, double f, double g, double h) {
return
// + x*3*
(-2*a*d*e*e - 7*d*d*e*e + 15*a*d*e*f + 21*d*d*e*f - 9*a*d*f*f - 18*d*d*f*f - 15*a*d*e*g -
3*d*d*e*g - 9*a*a*f*g + 9*d*d*f*g + 18*a*a*g*g + 9*a*d*g*g + 2*a*a*e*h - 2*d*d*e*h +
3*a*a*f*h + 15*a*d*f*h - 21*a*a*g*h - 15*a*d*g*h + 7*a*a*h*h + 2*a*d*h*h -
9*c*c*(2*e*e + 3*f*f + 3*f*h - 2*g*h + e*(-3*f - 4*g + h)) +
9*b*b*(3*g*g - 3*g*h + 2*h*(-2*f + h) + e*(-2*f + 3*g + h)) +
3*b*(3*c*(e*e + 3*e*(f - 3*g) + (9*f - 3*g - h)*h) + a*(6*f*f + e*g - 9*f*g - 9*g*g - 5*e*h + 9*f*h + 14*g*h - 7*h*h) +
d*(-e*e + 12*f*f - 27*f*g + e*(-9*f + 20*g - 5*h) + g*(9*g + h))) +
3*c*(a*(-(e*f) - 9*f*f + 27*f*g - 12*g*g + 5*e*h - 20*f*h + 9*g*h + h*h) +
d*(7*e*e + 9*f*f + 9*f*g - 6*g*g - f*h + e*(-14*f - 9*g + 5*h)))) // *y
;
}
// - x*3*Power(a - 3*b + 3*c - d,2)*(e - 3*f + 3*g - h)*Power(y,2)
static double calc_xyy(double a3b3cd, double e3f3gh) {
return -3 * a3b3cd * a3b3cd * e3f3gh;
}
#endif
static double (*calc_proc[])(double a, double b, double c, double d,
double e, double f, double g, double h) = {
calc_xx, calc_xy, calc_yy, calc_x, calc_y, calc_c
};
#if USE_SYVESTER
/* Control points to parametric coefficients
s = 1 - t
Attt + 3Btts + 3Ctss + Dsss ==
Attt + 3B(1 - t)tt + 3C(1 - t)(t - tt) + D(1 - t)(1 - 2t + tt) ==
Attt + 3B(tt - ttt) + 3C(t - tt - tt + ttt) + D(1-2t+tt-t+2tt-ttt) ==
Attt + 3Btt - 3Bttt + 3Ct - 6Ctt + 3Cttt + D - 3Dt + 3Dtt - Dttt ==
D + (3C - 3D)t + (3B - 6C + 3D)tt + (A - 3B + 3C - D)ttt
a = A - 3*B + 3*C - D
b = 3*B - 6*C + 3*D
c = 3*C - 3*D
d = D
*/
/* http://www.algorithmist.net/bezier3.html
p = 3 * A
q = 3 * B
r = 3 * C
a = A
b = q - p
c = p - 2 * q + r
d = D - A + q - r
B(t) = a + t * (b + t * (c + t * d))
so
B(t) = a + t*b + t*t*(c + t*d)
= a + t*b + t*t*c + t*t*t*d
*/
static void set_abcd(const double* cubic, double& a, double& b, double& c,
double& d) {
a = cubic[0]; // a = A
b = 3 * cubic[2]; // b = 3*B (compute rest of b lazily)
c = 3 * cubic[4]; // c = 3*C (compute rest of c lazily)
d = cubic[6]; // d = D
a += -b + c - d; // a = A - 3*B + 3*C - D
}
static void calc_bc(const double d, double& b, double& c) {
b -= 3 * c; // b = 3*B - 3*C
c -= 3 * d; // c = 3*C - 3*D
b -= c; // b = 3*B - 6*C + 3*D
}
static void alt_set_abcd(const double* cubic, double& a, double& b, double& c,
double& d) {
a = cubic[0];
double p = 3 * a;
double q = 3 * cubic[2];
double r = 3 * cubic[4];
b = q - p;
c = p - 2 * q + r;
d = cubic[6] - a + q - r;
}
const bool try_alt = true;
#else
static void calc_ABCD(double a, double b, double c, double d,
double e, double f, double g, double h,
double p[coeff_count]) {
double a3b3cd = a - 3 * (b - c) - d;
double e3f3gh = e - 3 * (f - g) - h;
p[xxx_coeff] = calc_xxx(e3f3gh);
p[xxy_coeff] = calc_xxy(a3b3cd, e3f3gh);
p[xyy_coeff] = calc_xyy(a3b3cd, e3f3gh);
p[yyy_coeff] = calc_yyy(a3b3cd);
}
#endif
bool implicit_matches(const Cubic& one, const Cubic& two) {
double p1[coeff_count]; // a'xxx , b'xxy , c'xyy , d'xx , e'xy , f'yy, etc.
double p2[coeff_count];
#if USE_SYVESTER
double a1, b1, c1, d1;
if (try_alt)
alt_set_abcd(&one[0].x, a1, b1, c1, d1);
else
set_abcd(&one[0].x, a1, b1, c1, d1);
double e1, f1, g1, h1;
if (try_alt)
alt_set_abcd(&one[0].y, e1, f1, g1, h1);
else
set_abcd(&one[0].y, e1, f1, g1, h1);
calc_ABCD(a1, e1, p1);
double a2, b2, c2, d2;
if (try_alt)
alt_set_abcd(&two[0].x, a2, b2, c2, d2);
else
set_abcd(&two[0].x, a2, b2, c2, d2);
double e2, f2, g2, h2;
if (try_alt)
alt_set_abcd(&two[0].y, e2, f2, g2, h2);
else
set_abcd(&two[0].y, e2, f2, g2, h2);
calc_ABCD(a2, e2, p2);
#else
double a1 = one[0].x;
double b1 = one[1].x;
double c1 = one[2].x;
double d1 = one[3].x;
double e1 = one[0].y;
double f1 = one[1].y;
double g1 = one[2].y;
double h1 = one[3].y;
calc_ABCD(a1, b1, c1, d1, e1, f1, g1, h1, p1);
double a2 = two[0].x;
double b2 = two[1].x;
double c2 = two[2].x;
double d2 = two[3].x;
double e2 = two[0].y;
double f2 = two[1].y;
double g2 = two[2].y;
double h2 = two[3].y;
calc_ABCD(a2, b2, c2, d2, e2, f2, g2, h2, p2);
#endif
int first = 0;
for (int index = 0; index < coeff_count; ++index) {
#if USE_SYVESTER
if (!try_alt && index == xx_coeff) {
calc_bc(d1, b1, c1);
calc_bc(h1, f1, g1);
calc_bc(d2, b2, c2);
calc_bc(h2, f2, g2);
}
#endif
if (index >= xx_coeff) {
int procIndex = index - xx_coeff;
p1[index] = (*calc_proc[procIndex])(a1, b1, c1, d1, e1, f1, g1, h1);
p2[index] = (*calc_proc[procIndex])(a2, b2, c2, d2, e2, f2, g2, h2);
}
if (approximately_zero(p1[index]) || approximately_zero(p2[index])) {
first += first == index;
continue;
}
if (first == index) {
continue;
}
if (!AlmostEqualUlps(p1[index] * p2[first], p1[first] * p2[index])) {
return false;
}
}
return true;
}
static double tangent(const double* cubic, double t) {
double a, b, c, d;
#if USE_SYVESTER
set_abcd(cubic, a, b, c, d);
calc_bc(d, b, c);
#else
coefficients(cubic, a, b, c, d);
#endif
return 3 * a * t * t + 2 * b * t + c;
}
void tangent(const Cubic& cubic, double t, _Point& result) {
result.x = tangent(&cubic[0].x, t);
result.y = tangent(&cubic[0].y, t);
}
// unit test to return and validate parametric coefficients
#include "CubicParameterization_TestUtility.cpp"

View File

@ -1,405 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <vector>
/* Given:
* Resultant[a*t^3 + b*t^2 + c*t + d - x, e*t^3 + f*t^2 + g*t + h - y, t]
*/
const char result1[] =
"-d^3 e^3 + c d^2 e^2 f - b d^2 e f^2 + a d^2 f^3 - c^2 d e^2 g + "
" 2 b d^2 e^2 g + b c d e f g - 3 a d^2 e f g - a c d f^2 g - "
" b^2 d e g^2 + 2 a c d e g^2 + a b d f g^2 - a^2 d g^3 + c^3 e^2 h - "
" 3 b c d e^2 h + 3 a d^2 e^2 h - b c^2 e f h + 2 b^2 d e f h + "
" a c d e f h + a c^2 f^2 h - 2 a b d f^2 h + b^2 c e g h - "
" 2 a c^2 e g h - a b d e g h - a b c f g h + 3 a^2 d f g h + "
" a^2 c g^2 h - b^3 e h^2 + 3 a b c e h^2 - 3 a^2 d e h^2 + "
" a b^2 f h^2 - 2 a^2 c f h^2 - a^2 b g h^2 + a^3 h^3 + 3 d^2 e^3 x - "
" 2 c d e^2 f x + 2 b d e f^2 x - 2 a d f^3 x + c^2 e^2 g x - "
" 4 b d e^2 g x - b c e f g x + 6 a d e f g x + a c f^2 g x + "
" b^2 e g^2 x - 2 a c e g^2 x - a b f g^2 x + a^2 g^3 x + "
" 3 b c e^2 h x - 6 a d e^2 h x - 2 b^2 e f h x - a c e f h x + "
" 2 a b f^2 h x + a b e g h x - 3 a^2 f g h x + 3 a^2 e h^2 x - "
" 3 d e^3 x^2 + c e^2 f x^2 - b e f^2 x^2 + a f^3 x^2 + "
" 2 b e^2 g x^2 - 3 a e f g x^2 + 3 a e^2 h x^2 + e^3 x^3 - "
" c^3 e^2 y + 3 b c d e^2 y - 3 a d^2 e^2 y + b c^2 e f y - "
" 2 b^2 d e f y - a c d e f y - a c^2 f^2 y + 2 a b d f^2 y - "
" b^2 c e g y + 2 a c^2 e g y + a b d e g y + a b c f g y - "
" 3 a^2 d f g y - a^2 c g^2 y + 2 b^3 e h y - 6 a b c e h y + "
" 6 a^2 d e h y - 2 a b^2 f h y + 4 a^2 c f h y + 2 a^2 b g h y - "
" 3 a^3 h^2 y - 3 b c e^2 x y + 6 a d e^2 x y + 2 b^2 e f x y + "
" a c e f x y - 2 a b f^2 x y - a b e g x y + 3 a^2 f g x y - "
" 6 a^2 e h x y - 3 a e^2 x^2 y - b^3 e y^2 + 3 a b c e y^2 - "
" 3 a^2 d e y^2 + a b^2 f y^2 - 2 a^2 c f y^2 - a^2 b g y^2 + "
" 3 a^3 h y^2 + 3 a^2 e x y^2 - a^3 y^3";
const size_t len1 = sizeof(result1) - 1;
/* Given:
* Expand[
* Det[{{a, b, c, (d - x), 0, 0},
* {0, a, b, c, (d - x), 0},
* {0, 0, a, b, c, (d - x)},
* {e, f, g, (h - y), 0, 0},
* {0, e, f, g, (h - y), 0},
* {0, 0, e, f, g, (h - y)}}]]
*/
// result1 and result2 are the same. 102 factors:
const char result2[] =
"-d^3 e^3 + c d^2 e^2 f - b d^2 e f^2 + a d^2 f^3 - c^2 d e^2 g + "
" 2 b d^2 e^2 g + b c d e f g - 3 a d^2 e f g - a c d f^2 g - "
" b^2 d e g^2 + 2 a c d e g^2 + a b d f g^2 - a^2 d g^3 + c^3 e^2 h - "
" 3 b c d e^2 h + 3 a d^2 e^2 h - b c^2 e f h + 2 b^2 d e f h + "
" a c d e f h + a c^2 f^2 h - 2 a b d f^2 h + b^2 c e g h - "
" 2 a c^2 e g h - a b d e g h - a b c f g h + 3 a^2 d f g h + "
" a^2 c g^2 h - b^3 e h^2 + 3 a b c e h^2 - 3 a^2 d e h^2 + "
" a b^2 f h^2 - 2 a^2 c f h^2 - a^2 b g h^2 + a^3 h^3 + 3 d^2 e^3 x - "
" 2 c d e^2 f x + 2 b d e f^2 x - 2 a d f^3 x + c^2 e^2 g x - "
" 4 b d e^2 g x - b c e f g x + 6 a d e f g x + a c f^2 g x + "
" b^2 e g^2 x - 2 a c e g^2 x - a b f g^2 x + a^2 g^3 x + "
" 3 b c e^2 h x - 6 a d e^2 h x - 2 b^2 e f h x - a c e f h x + "
" 2 a b f^2 h x + a b e g h x - 3 a^2 f g h x + 3 a^2 e h^2 x - "
" 3 d e^3 x^2 + c e^2 f x^2 - b e f^2 x^2 + a f^3 x^2 + "
" 2 b e^2 g x^2 - 3 a e f g x^2 + 3 a e^2 h x^2 + e^3 x^3 - "
" c^3 e^2 y + 3 b c d e^2 y - 3 a d^2 e^2 y + b c^2 e f y - "
" 2 b^2 d e f y - a c d e f y - a c^2 f^2 y + 2 a b d f^2 y - "
" b^2 c e g y + 2 a c^2 e g y + a b d e g y + a b c f g y - "
" 3 a^2 d f g y - a^2 c g^2 y + 2 b^3 e h y - 6 a b c e h y + "
" 6 a^2 d e h y - 2 a b^2 f h y + 4 a^2 c f h y + 2 a^2 b g h y - "
" 3 a^3 h^2 y - 3 b c e^2 x y + 6 a d e^2 x y + 2 b^2 e f x y + "
" a c e f x y - 2 a b f^2 x y - a b e g x y + 3 a^2 f g x y - "
" 6 a^2 e h x y - 3 a e^2 x^2 y - b^3 e y^2 + 3 a b c e y^2 - "
" 3 a^2 d e y^2 + a b^2 f y^2 - 2 a^2 c f y^2 - a^2 b g y^2 + "
" 3 a^3 h y^2 + 3 a^2 e x y^2 - a^3 y^3";
const size_t len2 = sizeof(result2) - 1;
/* Given: r1 = Resultant[
* a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - x,
* e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, t]
* Collect[r1, {x, y}, Simplify]
* CForm[%]
* then use regex to replace Power\(([a-h]),3\) with \1*\1*\1
* and Power\(([a-h]),2\) with \1*\1
* yields:
d*d*d*e*e*e - 3*d*d*(3*c*e*e*f + 3*b*e*(-3*f*f + 2*e*g) + a*(9*f*f*f - 9*e*f*g + e*e*h)) -
h*(27*c*c*c*e*e - 27*c*c*(3*b*e*f - 3*a*f*f + 2*a*e*g) +
h*(-27*b*b*b*e + 27*a*b*b*f - 9*a*a*b*g + a*a*a*h) +
9*c*(9*b*b*e*g + a*b*(-9*f*g + 3*e*h) + a*a*(3*g*g - 2*f*h))) +
3*d*(9*c*c*e*e*g + 9*b*b*e*(3*g*g - 2*f*h) + 3*a*b*(-9*f*g*g + 6*f*f*h + e*g*h) +
a*a*(9*g*g*g - 9*f*g*h + e*h*h) + 3*c*(3*b*e*(-3*f*g + e*h) + a*(9*f*f*g - 6*e*g*g - e*f*h)))
- Power(e - 3*f + 3*g - h,3)*Power(x,3)
+ 3*(6*b*d*d*e*e - d*d*d*e*e + 18*b*b*d*e*f - 18*b*d*d*e*f -
9*b*d*d*f*f - 54*b*b*d*e*g + 12*b*d*d*e*g - 27*b*b*d*g*g - 18*b*b*b*e*h + 18*b*b*d*e*h +
18*b*b*d*f*h + a*a*a*h*h - 9*b*b*b*h*h + 9*c*c*c*e*(e + 2*h) +
a*a*(-3*b*h*(2*g + h) + d*(-27*g*g + 9*g*h - h*(2*e + h) + 9*f*(g + h))) +
a*(9*b*b*h*(2*f + h) - 3*b*d*(6*f*f - 6*f*(3*g - 2*h) + g*(-9*g + h) + e*(g + h)) +
d*d*(e*e + 9*f*(3*f - g) + e*(-9*f - 9*g + 2*h))) -
9*c*c*(d*e*(e + 2*g) + 3*b*(f*h + e*(f + h)) + a*(-3*f*f - 6*f*h + 2*(g*h + e*(g + h)))) +
3*c*(d*d*e*(e + 2*f) + a*a*(3*g*g + 6*g*h - 2*h*(2*f + h)) + 9*b*b*(g*h + e*(g + h)) +
a*d*(-9*f*f - 18*f*g + 6*g*g + f*h + e*(f + 12*g + h)) +
b*(d*(-3*e*e + 9*f*g + e*(9*f + 9*g - 6*h)) + 3*a*(h*(2*e - 3*g + h) - 3*f*(g + h)))))*y
- 3*(18*c*c*c*e - 18*c*c*d*e + 6*c*d*d*e - d*d*d*e + 3*c*d*d*f - 9*c*c*d*g + a*a*a*h + 9*c*c*c*h -
9*b*b*b*(e + 2*h) - a*a*(d*(e - 9*f + 18*g - 7*h) + 3*c*(2*f - 6*g + h)) +
a*(-9*c*c*(2*e - 6*f + 2*g - h) + d*d*(-7*e + 18*f - 9*g + h) + 3*c*d*(7*e - 17*f + 3*g + h)) +
9*b*b*(3*c*(e + g + h) + a*(f + 2*h) - d*(e - 2*(f - 3*g + h))) -
3*b*(-(d*d*(e - 6*f + 2*g)) - 3*c*d*(e + 3*f + 3*g - h) + 9*c*c*(e + f + h) + a*a*(g + 2*h) +
a*(c*(-3*e + 9*f + 9*g + 3*h) + d*(e + 3*f - 17*g + 7*h))))*Power(y,2)
+ Power(a - 3*b + 3*c - d,3)*Power(y,3)
+ Power(x,2)*(-3*(-9*b*e*f*f + 9*a*f*f*f + 6*b*e*e*g - 9*a*e*f*g + 27*b*e*f*g - 27*a*f*f*g + 18*a*e*g*g - 54*b*e*g*g +
27*a*f*g*g + 27*b*f*g*g - 18*a*g*g*g + a*e*e*h - 9*b*e*e*h + 3*a*e*f*h + 9*b*e*f*h + 9*a*f*f*h -
18*b*f*f*h - 21*a*e*g*h + 51*b*e*g*h - 9*a*f*g*h - 27*b*f*g*h + 18*a*g*g*h + 7*a*e*h*h - 18*b*e*h*h - 3*a*f*h*h +
18*b*f*h*h - 6*a*g*h*h - 3*b*g*h*h + a*h*h*h +
3*c*(-9*f*f*(g - 2*h) + 3*g*g*h - f*h*(9*g + 2*h) + e*e*(f - 6*g + 6*h) +
e*(9*f*g + 6*g*g - 17*f*h - 3*g*h + 3*h*h)) -
d*(e*e*e + e*e*(-6*f - 3*g + 7*h) - 9*(2*f - g)*(f*f + g*g - f*(g + h)) +
e*(18*f*f + 9*g*g + 3*g*h + h*h - 3*f*(3*g + 7*h)))) )
+ Power(x,2)*(3*(a - 3*b + 3*c - d)*Power(e - 3*f + 3*g - h,2)*y)
+ x*(-3*(27*b*b*e*g*g - 27*a*b*f*g*g + 9*a*a*g*g*g - 18*b*b*e*f*h + 18*a*b*f*f*h + 3*a*b*e*g*h -
27*b*b*e*g*h - 9*a*a*f*g*h + 27*a*b*f*g*h - 9*a*a*g*g*h + a*a*e*h*h - 9*a*b*e*h*h +
27*b*b*e*h*h + 6*a*a*f*h*h - 18*a*b*f*h*h - 9*b*b*f*h*h + 3*a*a*g*h*h +
6*a*b*g*h*h - a*a*h*h*h + 9*c*c*(e*e*(g - 3*h) - 3*f*f*h + e*(3*f + 2*g)*h) +
d*d*(e*e*e - 9*f*f*f + 9*e*f*(f + g) - e*e*(3*f + 6*g + h)) +
d*(-3*c*(-9*f*f*g + e*e*(2*f - 6*g - 3*h) + e*(9*f*g + 6*g*g + f*h)) +
a*(-18*f*f*f - 18*e*g*g + 18*g*g*g - 2*e*e*h + 3*e*g*h + 2*e*h*h + 9*f*f*(3*g + 2*h) +
3*f*(6*e*g - 9*g*g - e*h - 6*g*h)) - 3*b*(9*f*g*g + e*e*(4*g - 3*h) - 6*f*f*h -
e*(6*f*f + g*(18*g + h) - 3*f*(3*g + 4*h)))) +
3*c*(3*b*(e*e*h + 3*f*g*h - e*(3*f*g - 6*f*h + 6*g*h + h*h)) +
a*(9*f*f*(g - 2*h) + f*h*(-e + 9*g + 4*h) - 3*(2*g*g*h + e*(2*g*g - 4*g*h + h*h))))) )
+ x*3*(-2*a*d*e*e - 7*d*d*e*e + 15*a*d*e*f + 21*d*d*e*f - 9*a*d*f*f - 18*d*d*f*f - 15*a*d*e*g -
3*d*d*e*g - 9*a*a*f*g + 9*d*d*f*g + 18*a*a*g*g + 9*a*d*g*g + 2*a*a*e*h - 2*d*d*e*h +
3*a*a*f*h + 15*a*d*f*h - 21*a*a*g*h - 15*a*d*g*h + 7*a*a*h*h + 2*a*d*h*h -
9*c*c*(2*e*e + 3*f*f + 3*f*h - 2*g*h + e*(-3*f - 4*g + h)) +
9*b*b*(3*g*g - 3*g*h + 2*h*(-2*f + h) + e*(-2*f + 3*g + h)) +
3*b*(3*c*(e*e + 3*e*(f - 3*g) + (9*f - 3*g - h)*h) + a*(6*f*f + e*g - 9*f*g - 9*g*g - 5*e*h + 9*f*h + 14*g*h - 7*h*h) +
d*(-e*e + 12*f*f - 27*f*g + e*(-9*f + 20*g - 5*h) + g*(9*g + h))) +
3*c*(a*(-(e*f) - 9*f*f + 27*f*g - 12*g*g + 5*e*h - 20*f*h + 9*g*h + h*h) +
d*(7*e*e + 9*f*f + 9*f*g - 6*g*g - f*h + e*(-14*f - 9*g + 5*h))))*y
- x*3*Power(a - 3*b + 3*c - d,2)*(e - 3*f + 3*g - h)*Power(y,2)
*/
const int factors = 8;
struct coeff {
int s; // constant and coefficient sign
int n[factors]; // 0 or power of a (1, 2, or 3) for a through h
};
enum {
xxx_coeff,
xxy_coeff,
xyy_coeff,
yyy_coeff,
xx_coeff,
xy_coeff,
yy_coeff,
x_coeff,
y_coeff,
c_coeff,
coeff_count
};
typedef std::vector<coeff> coeffs;
typedef std::vector<coeffs> n_coeffs;
static char skipSpace(const char* str, size_t& index) {
do {
++index;
} while (str[index] == ' ');
return str[index];
}
static char backSkipSpace(const char* str, size_t& end) {
while (str[end - 1] == ' ') {
--end;
}
return str[end - 1];
}
static void match(const char* str, size_t len, coeffs& co, const char pattern[]) {
size_t patternLen = strlen(pattern);
size_t index = 0;
while (index < len) {
char ch = str[index];
if (ch != '-' && ch != '+') {
printf("missing sign\n");
}
size_t end = index + 1;
while (str[end] != '+' && str[end] != '-' && ++end < len) {
;
}
backSkipSpace(str, end);
size_t idx = index;
index = end;
skipSpace(str, index);
if (!strncmp(&str[end - patternLen], pattern, patternLen) == 0) {
continue;
}
size_t endCoeff = end - patternLen;
char last = backSkipSpace(str, endCoeff);
if (last == '2' || last == '3') {
last = str[endCoeff - 3]; // skip ^2
}
if (last == 'x' || last == 'y') {
continue;
}
coeff c;
c.s = str[idx] == '-' ? -1 : 1;
bzero(c.n, sizeof(c.n));
ch = skipSpace(str, idx);
if (ch >= '2' && ch <= '6') {
c.s *= ch - '0';
ch = skipSpace(str, idx);
}
while (idx < endCoeff) {
char x = str[idx];
if (x < 'a' || x > 'a' + factors) {
printf("expected factor\n");
}
idx++;
int pow = 1;
if (str[idx] == '^') {
idx++;
char exp = str[idx];
if (exp < '2' || exp > '3') {
printf("expected exponent\n");
}
pow = exp - '0';
}
skipSpace(str, idx);
c.n[x - 'a'] = pow;
}
co.push_back(c);
}
}
void cubecode_test(int test);
void cubecode_test(int test) {
const char* str = test ? result2 : result1;
size_t len = strlen(str);
n_coeffs c(coeff_count);
match(str, len, c[xxx_coeff], "x^3"); // 1 factor
match(str, len, c[xxy_coeff], "x^2 y"); // 1 factor
match(str, len, c[xyy_coeff], "x y^2"); // 1 factor
match(str, len, c[yyy_coeff], "y^3"); // 1 factor
match(str, len, c[xx_coeff], "x^2"); // 7 factors
match(str, len, c[xy_coeff], "x y"); // 8 factors
match(str, len, c[yy_coeff], "y^2"); // 7 factors
match(str, len, c[x_coeff], "x"); // 21 factors
match(str, len, c[y_coeff], "y"); // 21 factors
match(str, len, c[c_coeff], ""); // 34 factors : total 102
#define COMPUTE_MOST_FREQUENT_EXPRESSION_TRIPLETS 0
#define WRITE_AS_NONOPTIMIZED_C_CODE 0
#if COMPUTE_MOST_FREQUENT_EXPRESSION_TRIPLETS
int count[factors][factors][factors];
bzero(count, sizeof(count));
#endif
#if WRITE_AS_NONOPTIMIZED_C_CODE
printf("// start of generated code");
#endif
for (n_coeffs::iterator it = c.begin(); it < c.end(); ++it) {
coeffs& co = *it;
#if WRITE_AS_NONOPTIMIZED_C_CODE
printf("\nstatic double calc_%c(double a, double b, double c, double d,"
"\n double e, double f, double g, double h) {"
"\n return"
"\n ", 'A' + (it - c.begin()));
if (co[0].s > 0) {
printf(" ");
}
if (abs(co[0].s) == 1) {
printf(" ");
}
#endif
for (coeffs::iterator ct = co.begin(); ct < co.end(); ++ct) {
const coeff& cf = *ct;
#if WRITE_AS_NONOPTIMIZED_C_CODE
printf(" ");
bool firstFactor = false;
if (ct - co.begin() > 0 || cf.s < 0) {
printf("%c", cf.s < 0 ? '-' : '+');
}
if (ct - co.begin() > 0) {
printf(" ");
}
if (abs(cf.s) > 1) {
printf("%d * ", abs(cf.s));
} else {
if (ct - co.begin() > 0) {
printf(" ");
}
}
#endif
for (int x = 0; x < factors; ++x) {
if (cf.n[x] == 0) {
continue;
}
#if WRITE_AS_NONOPTIMIZED_C_CODE
for (int y = 0 ; y < cf.n[x]; ++y) {
if (y > 0 || firstFactor) {
printf(" * ");
}
printf("%c", 'a' + x);
}
firstFactor = true;
#endif
#if COMPUTE_MOST_FREQUENT_EXPRESSION_TRIPLETS
for (int y = x; y < factors; ++y) {
if (cf.n[y] == 0) {
continue;
}
if (x == y && cf.n[y] == 1) {
continue;
}
for (int z = y; z < factors; ++z) {
if (cf.n[z] == 0) {
continue;
}
if ((x == z || y == z) && cf.n[z] == 1) {
continue;
}
if (x == y && y == z && cf.n[z] == 2) {
continue;
}
count[x][y][z]++;
}
}
#endif
}
#if WRITE_AS_NONOPTIMIZED_C_CODE
if (ct + 1 < co.end()) {
printf("\n");
}
#endif
}
#if WRITE_AS_NONOPTIMIZED_C_CODE
printf(";\n}\n");
#endif
}
#if WRITE_AS_NONOPTIMIZED_C_CODE
printf("// end of generated code\n");
#endif
#if COMPUTE_MOST_FREQUENT_EXPRESSION_TRIPLETS
const int bestCount = 20;
int best[bestCount][4];
bzero(best, sizeof(best));
for (int x = 0; x < factors; ++x) {
for (int y = x; y < factors; ++y) {
for (int z = y; z < factors; ++z) {
if (!count[x][y][z]) {
continue;
}
for (int w = 0; w < bestCount; ++w) {
if (best[w][0] < count[x][y][z]) {
best[w][0] = count[x][y][z];
best[w][1] = x;
best[w][2] = y;
best[w][3] = z;
break;
}
}
}
}
}
for (int w = 0; w < bestCount; ++w) {
printf("%c%c%c=%d\n", 'a' + best[w][1], 'a' + best[w][2],
'a' + best[w][3], best[w][0]);
}
#endif
#if WRITE_AS_NONOPTIMIZED_C_CODE
printf("\n");
#endif
}
/* results: variable triplets used 10 or more times:
aah=14
ade=14
aeh=14
dee=14
bce=13
beg=13
beh=12
bbe=11
bef=11
cee=11
cef=11
def=11
ceh=10
deg=10
*/

View File

@ -1,113 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "QuadraticUtilities.h"
#include "CurveIntersection.h"
#include "Intersection_Tests.h"
#include "Parameterization_Test.h"
#include "TestUtilities.h"
const Quadratic quadratics[] = {
{{0, 0}, {1, 0}, {1, 1}},
};
const size_t quadratics_count = sizeof(quadratics) / sizeof(quadratics[0]);
int firstCubicCoincidenceTest = 0;
void CubicCoincidence_Test() {
// split large quadratic
// upscale quadratics to cubics
// compare original, parts, to see if the are coincident
for (size_t index = firstCubicCoincidenceTest; index < quadratics_count; ++index) {
const Quadratic& test = quadratics[index];
QuadraticPair split;
chop_at(test, split, 0.5);
Quadratic midThird;
sub_divide(test, 1.0/3, 2.0/3, midThird);
Cubic whole, first, second, mid;
quad_to_cubic(test, whole);
quad_to_cubic(split.first(), first);
quad_to_cubic(split.second(), second);
quad_to_cubic(midThird, mid);
if (!implicit_matches(whole, first)) {
SkDebugf("%s-1 %d\n", __FUNCTION__, (int)index);
}
if (!implicit_matches(whole, second)) {
SkDebugf("%s-2 %d\n", __FUNCTION__, (int)index);
}
if (!implicit_matches(mid, first)) {
SkDebugf("%s-3 %d\n", __FUNCTION__, (int)index);
}
if (!implicit_matches(mid, second)) {
SkDebugf("%s-4 %d\n", __FUNCTION__, (int)index);
}
if (!implicit_matches(first, second)) {
SkDebugf("%s-5 %d\n", __FUNCTION__, (int)index);
}
}
}
// pairs of coincident cubics
// The on curve points of each cubic should be on both parameterized cubics.
const Cubic cubics[] = {
{
{ 1, -1},
{ 1.0/3, 1},
{-1.0/3, -1},
{-1, 1}
},
{
{-1, 1},
{-1.0/3, -1},
{ 1.0/3, 1},
{ 1, -1}
},
{
{0, 2},
{0, 1},
{1, 0},
{2, 0}
}, {
{2, 0},
{1, 0},
{0, 1},
{0, 2}
},
{
{315.74799999999999, 312.83999999999997},
{312.64400000000001, 318.13400000000001},
{305.83600000000001, 319.90899999999999},
{300.54199999999997, 316.80399999999997}
}, {
{317.12200000000001, 309.05000000000001},
{316.11200000000002, 315.10199999999998},
{310.38499999999999, 319.19},
{304.33199999999999, 318.17899999999997}
}
};
const size_t cubics_count = sizeof(cubics) / sizeof(cubics[0]);
int firstCubicParameterizationTest = 0;
void CubicParameterization_Test() {
for (size_t index = firstCubicParameterizationTest; index < cubics_count; ++index) {
for (size_t inner = 0; inner < 4; inner += 3) {
if (!point_on_parameterized_curve(cubics[index], cubics[index][inner])) {
SkDebugf("%s [%zu,%zu] 1 parameterization failed\n",
__FUNCTION__, index, inner);
}
if (!point_on_parameterized_curve(cubics[index], cubics[index ^ 1][inner])) {
SkDebugf("%s [%zu,%zu] 2 parameterization failed\n",
__FUNCTION__, index, inner);
}
}
if (!implicit_matches(cubics[index], cubics[index ^ 1])) {
SkDebugf("%s %d\n", __FUNCTION__, (int)index);
}
}
}

View File

@ -1,53 +0,0 @@
// included by CubicParameterization.cpp
// accesses internal functions to validate parameterized coefficients
#include "Parameterization_Test.h"
static void parameter_coeffs(const Cubic& cubic, double coeffs[coeff_count]) {
#if USE_SYVESTER
double ax, bx, cx, dx;
if (try_alt)
alt_set_abcd(&cubic[0].x, ax, bx, cx, dx);
else
set_abcd(&cubic[0].x, ax, bx, cx, dx);
double ay, by, cy, dy;
if (try_alt)
alt_set_abcd(&cubic[0].y, ay, by, cy, dy);
else
set_abcd(&cubic[0].y, ay, by, cy, dy);
calc_ABCD(ax, ay, coeffs);
if (!try_alt) calc_bc(dx, bx, cx);
if (!try_alt) calc_bc(dy, by, cy);
#else
double ax = cubic[0].x;
double bx = cubic[1].x;
double cx = cubic[2].x;
double dx = cubic[3].x;
double ay = cubic[0].y;
double by = cubic[1].y;
double cy = cubic[2].y;
double dy = cubic[3].y;
calc_ABCD(ax, bx, cx, dx, ay, by, cy, dy, coeffs);
#endif
for (int index = xx_coeff; index < coeff_count; ++index) {
int procIndex = index - xx_coeff;
coeffs[index] = (*calc_proc[procIndex])(ax, bx, cx, dx, ay, by, cy, dy);
}
}
bool point_on_parameterized_curve(const Cubic& cubic, const _Point& point) {
double coeffs[coeff_count];
parameter_coeffs(cubic, coeffs);
double xxx = coeffs[xxx_coeff] * point.x * point.x * point.x;
double xxy = coeffs[xxy_coeff] * point.x * point.x * point.y;
double xyy = coeffs[xyy_coeff] * point.x * point.y * point.y;
double yyy = coeffs[yyy_coeff] * point.y * point.y * point.y;
double xx = coeffs[ xx_coeff] * point.x * point.x;
double xy = coeffs[ xy_coeff] * point.x * point.y;
double yy = coeffs[ yy_coeff] * point.y * point.y;
double x = coeffs[ x_coeff] * point.x;
double y = coeffs[ y_coeff] * point.y;
double c = coeffs[ c_coeff];
double sum = xxx + xxy + xyy + yyy + xx + xy + yy + x + y + c;
return approximately_zero(sum);
}

View File

@ -1,254 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Extrema.h"
#include "IntersectionUtilities.h"
#include "LineParameters.h"
static double interp_cubic_coords(const double* src, double t)
{
double ab = interp(src[0], src[2], t);
double bc = interp(src[2], src[4], t);
double cd = interp(src[4], src[6], t);
double abc = interp(ab, bc, t);
double bcd = interp(bc, cd, t);
return interp(abc, bcd, t);
}
static int coincident_line(const Cubic& cubic, Cubic& reduction) {
reduction[0] = reduction[1] = cubic[0];
return 1;
}
static int vertical_line(const Cubic& cubic, ReduceOrder_Styles reduceStyle, Cubic& reduction) {
double tValues[2];
reduction[0] = cubic[0];
reduction[1] = cubic[3];
if (reduceStyle == kReduceOrder_TreatAsFill) {
return 2;
}
int smaller = reduction[1].y > reduction[0].y;
int larger = smaller ^ 1;
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) {
reduction[smaller].y = yExtrema;
continue;
}
if (reduction[larger].y < yExtrema) {
reduction[larger].y = yExtrema;
}
}
return 2;
}
static int horizontal_line(const Cubic& cubic, ReduceOrder_Styles reduceStyle, Cubic& reduction) {
double tValues[2];
reduction[0] = cubic[0];
reduction[1] = cubic[3];
if (reduceStyle == kReduceOrder_TreatAsFill) {
return 2;
}
int smaller = reduction[1].x > reduction[0].x;
int larger = smaller ^ 1;
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) {
reduction[smaller].x = xExtrema;
continue;
}
if (reduction[larger].x < xExtrema) {
reduction[larger].x = xExtrema;
}
}
return 2;
}
// check to see if it is a quadratic or a line
static int check_quadratic(const Cubic& cubic, Cubic& reduction) {
double dx10 = cubic[1].x - cubic[0].x;
double dx23 = cubic[2].x - cubic[3].x;
double midX = cubic[0].x + dx10 * 3 / 2;
if (!AlmostEqualUlps(midX - cubic[3].x, dx23 * 3 / 2)) {
return 0;
}
double dy10 = cubic[1].y - cubic[0].y;
double dy23 = cubic[2].y - cubic[3].y;
double midY = cubic[0].y + dy10 * 3 / 2;
if (!AlmostEqualUlps(midY - cubic[3].y, dy23 * 3 / 2)) {
return 0;
}
reduction[0] = cubic[0];
reduction[1].x = midX;
reduction[1].y = midY;
reduction[2] = cubic[3];
return 3;
}
static int check_linear(const Cubic& cubic, ReduceOrder_Styles reduceStyle,
int minX, int maxX, int minY, int maxY, Cubic& reduction) {
int startIndex = 0;
int endIndex = 3;
while (cubic[startIndex].approximatelyEqual(cubic[endIndex])) {
--endIndex;
if (endIndex == 0) {
printf("%s shouldn't get here if all four points are about equal\n", __FUNCTION__);
SkASSERT(0);
}
}
if (!isLinear(cubic, startIndex, endIndex)) {
return 0;
}
// four are colinear: return line formed by outside
reduction[0] = cubic[0];
reduction[1] = cubic[3];
if (reduceStyle == kReduceOrder_TreatAsFill) {
return 2;
}
int sameSide1;
int sameSide2;
bool useX = cubic[maxX].x - cubic[minX].x >= cubic[maxY].y - cubic[minY].y;
if (useX) {
sameSide1 = sign(cubic[0].x - cubic[1].x) + sign(cubic[3].x - cubic[1].x);
sameSide2 = sign(cubic[0].x - cubic[2].x) + sign(cubic[3].x - cubic[2].x);
} else {
sameSide1 = sign(cubic[0].y - cubic[1].y) + sign(cubic[3].y - cubic[1].y);
sameSide2 = sign(cubic[0].y - cubic[2].y) + sign(cubic[3].y - cubic[2].y);
}
if (sameSide1 == sameSide2 && (sameSide1 & 3) != 2) {
return 2;
}
double tValues[2];
int roots;
if (useX) {
roots = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
} else {
roots = findExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
}
for (int index = 0; index < roots; ++index) {
_Point extrema;
extrema.x = interp_cubic_coords(&cubic[0].x, tValues[index]);
extrema.y = interp_cubic_coords(&cubic[0].y, tValues[index]);
// sameSide > 0 means mid is smaller than either [0] or [3], so replace smaller
int replace;
if (useX) {
if (extrema.x < cubic[0].x ^ extrema.x < cubic[3].x) {
continue;
}
replace = (extrema.x < cubic[0].x | extrema.x < cubic[3].x)
^ (cubic[0].x < cubic[3].x);
} else {
if (extrema.y < cubic[0].y ^ extrema.y < cubic[3].y) {
continue;
}
replace = (extrema.y < cubic[0].y | extrema.y < cubic[3].y)
^ (cubic[0].y < cubic[3].y);
}
reduction[replace] = extrema;
}
return 2;
}
bool isLinear(const Cubic& cubic, int startIndex, int endIndex) {
LineParameters lineParameters;
lineParameters.cubicEndPoints(cubic, startIndex, endIndex);
// FIXME: maybe it's possible to avoid this and compare non-normalized
lineParameters.normalize();
double distance = lineParameters.controlPtDistance(cubic, 1);
if (!approximately_zero(distance)) {
return false;
}
distance = lineParameters.controlPtDistance(cubic, 2);
return approximately_zero(distance);
}
/* food for thought:
http://objectmix.com/graphics/132906-fast-precision-driven-cubic-quadratic-piecewise-degree-reduction-algos-2-a.html
Given points c1, c2, c3 and c4 of a cubic Bezier, the points of the
corresponding quadratic Bezier are (given in convex combinations of
points):
q1 = (11/13)c1 + (3/13)c2 -(3/13)c3 + (2/13)c4
q2 = -c1 + (3/2)c2 + (3/2)c3 - c4
q3 = (2/13)c1 - (3/13)c2 + (3/13)c3 + (11/13)c4
Of course, this curve does not interpolate the end-points, but it would
be interesting to see the behaviour of such a curve in an applet.
--
Kalle Rutanen
http://kaba.hilvi.org
*/
// reduce to a quadratic or smaller
// look for identical points
// look for all four points in a line
// note that three points in a line doesn't simplify a cubic
// look for approximation with single quadratic
// save approximation with multiple quadratics for later
int reduceOrder(const Cubic& cubic, Cubic& reduction, ReduceOrder_Quadratics allowQuadratics,
ReduceOrder_Styles reduceStyle) {
int index, minX, maxX, minY, maxY;
int minXSet, minYSet;
minX = maxX = minY = maxY = 0;
minXSet = minYSet = 0;
for (index = 1; index < 4; ++index) {
if (cubic[minX].x > cubic[index].x) {
minX = index;
}
if (cubic[minY].y > cubic[index].y) {
minY = index;
}
if (cubic[maxX].x < cubic[index].x) {
maxX = index;
}
if (cubic[maxY].y < cubic[index].y) {
maxY = index;
}
}
for (index = 0; index < 4; ++index) {
double cx = cubic[index].x;
double cy = cubic[index].y;
double denom = SkTMax(fabs(cx), SkTMax(fabs(cy),
SkTMax(fabs(cubic[minX].x), fabs(cubic[minY].y))));
if (denom == 0) {
minXSet |= 1 << index;
minYSet |= 1 << index;
continue;
}
double inv = 1 / denom;
if (approximately_equal_half(cx * inv, cubic[minX].x * inv)) {
minXSet |= 1 << index;
}
if (approximately_equal_half(cy * inv, cubic[minY].y * inv)) {
minYSet |= 1 << index;
}
}
if (minXSet == 0xF) { // test for vertical line
if (minYSet == 0xF) { // return 1 if all four are coincident
return coincident_line(cubic, reduction);
}
return vertical_line(cubic, reduceStyle, reduction);
}
if (minYSet == 0xF) { // test for horizontal line
return horizontal_line(cubic, reduceStyle, reduction);
}
int result = check_linear(cubic, reduceStyle, minX, maxX, minY, maxY, reduction);
if (result) {
return result;
}
if (allowQuadratics == kReduceOrder_QuadraticsAllowed
&& (result = check_quadratic(cubic, reduction))) {
return result;
}
memcpy(reduction, cubic, sizeof(Cubic));
return 4;
}

View File

@ -1,154 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CubicIntersection_TestData.h"
#include "Intersection_Tests.h"
#include "QuadraticIntersection_TestData.h"
#include "TestUtilities.h"
void CubicReduceOrder_Test() {
size_t index;
Cubic reduce;
int order;
enum {
RunAll,
RunPointDegenerates,
RunNotPointDegenerates,
RunLines,
RunNotLines,
RunModEpsilonLines,
RunLessEpsilonLines,
RunNegEpsilonLines,
RunQuadraticLines,
RunQuadraticModLines,
RunComputedLines,
RunNone
} run = RunAll;
int firstTestIndex = 0;
#if 0
run = RunComputedLines;
firstTestIndex = 18;
#endif
int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates ? firstTestIndex : SK_MaxS32;
int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates ? firstTestIndex : SK_MaxS32;
int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : SK_MaxS32;
int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : SK_MaxS32;
int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines ? firstTestIndex : SK_MaxS32;
int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines ? firstTestIndex : SK_MaxS32;
int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines ? firstTestIndex : SK_MaxS32;
int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : SK_MaxS32;
int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : SK_MaxS32;
int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines ? firstTestIndex : SK_MaxS32;
for (index = firstPointDegeneratesTest; index < pointDegenerates_count; ++index) {
const Cubic& cubic = pointDegenerates[index];
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order != 1) {
SkDebugf("[%d] pointDegenerates order=%d\n", (int) index, order);
}
}
for (index = firstNotPointDegeneratesTest; index < notPointDegenerates_count; ++index) {
const Cubic& cubic = notPointDegenerates[index];
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order == 1) {
SkDebugf("[%d] notPointDegenerates order=%d\n", (int) index, order);
}
}
for (index = firstLinesTest; index < lines_count; ++index) {
const Cubic& cubic = lines[index];
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order != 2) {
SkDebugf("[%d] lines order=%d\n", (int) index, order);
}
}
for (index = firstNotLinesTest; index < notLines_count; ++index) {
const Cubic& cubic = notLines[index];
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order == 2) {
SkDebugf("[%d] notLines order=%d\n", (int) index, order);
}
}
for (index = firstModEpsilonTest; index < modEpsilonLines_count; ++index) {
const Cubic& cubic = modEpsilonLines[index];
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order == 2) {
SkDebugf("[%d] line mod by epsilon order=%d\n", (int) index, order);
}
}
for (index = firstLessEpsilonTest; index < lessEpsilonLines_count; ++index) {
const Cubic& cubic = lessEpsilonLines[index];
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order != 2) {
SkDebugf("[%d] line less by epsilon/2 order=%d\n", (int) index, order);
}
}
for (index = firstNegEpsilonTest; index < negEpsilonLines_count; ++index) {
const Cubic& cubic = negEpsilonLines[index];
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order != 2) {
SkDebugf("[%d] line neg by epsilon/2 order=%d\n", (int) index, order);
}
}
for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
const Quadratic& quad = quadraticLines[index];
Cubic cubic;
quad_to_cubic(quad, cubic);
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order != 2) {
SkDebugf("[%d] line quad order=%d\n", (int) index, order);
}
}
for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
const Quadratic& quad = quadraticModEpsilonLines[index];
Cubic cubic;
quad_to_cubic(quad, cubic);
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order != 3) {
SkDebugf("[%d] line mod quad order=%d\n", (int) index, order);
}
}
// test if computed line end points are valid
for (index = firstComputedLinesTest; index < lines_count; ++index) {
const Cubic& cubic = lines[index];
bool controlsInside = controls_inside(cubic);
order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (reduce[0].x == reduce[1].x && reduce[0].y == reduce[1].y) {
SkDebugf("[%d] line computed ends match order=%d\n", (int) index, order);
}
if (controlsInside) {
if ( (reduce[0].x != cubic[0].x && reduce[0].x != cubic[3].x)
|| (reduce[0].y != cubic[0].y && reduce[0].y != cubic[3].y)
|| (reduce[1].x != cubic[0].x && reduce[1].x != cubic[3].x)
|| (reduce[1].y != cubic[0].y && reduce[1].y != cubic[3].y)) {
SkDebugf("[%d] line computed ends order=%d\n", (int) index, order);
}
} else {
// binary search for extrema, compare against actual results
// while a control point is outside of bounding box formed by end points, split
_Rect bounds = {DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX};
find_tight_bounds(cubic, bounds);
if ( (!AlmostEqualUlps(reduce[0].x, bounds.left) && !AlmostEqualUlps(reduce[0].x, bounds.right))
|| (!AlmostEqualUlps(reduce[0].y, bounds.top) && !AlmostEqualUlps(reduce[0].y, bounds.bottom))
|| (!AlmostEqualUlps(reduce[1].x, bounds.left) && !AlmostEqualUlps(reduce[1].x, bounds.right))
|| (!AlmostEqualUlps(reduce[1].y, bounds.top) && !AlmostEqualUlps(reduce[1].y, bounds.bottom))) {
SkDebugf("[%d] line computed tight bounds order=%d\n", (int) index, order);
}
}
}
}

View File

@ -1,147 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CubicUtilities.h"
#include "IntersectionUtilities.h"
/*
Given a cubic c, t1, and t2, find a small cubic segment.
The new cubic is defined as points A, B, C, and D, where
s1 = 1 - t1
s2 = 1 - t2
A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1
D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2
We don't have B or C. So We define two equations to isolate them.
First, compute two reference T values 1/3 and 2/3 from t1 to t2:
c(at (2*t1 + t2)/3) == E
c(at (t1 + 2*t2)/3) == F
Next, compute where those values must be if we know the values of B and C:
_12 = A*2/3 + B*1/3
12_ = A*1/3 + B*2/3
_23 = B*2/3 + C*1/3
23_ = B*1/3 + C*2/3
_34 = C*2/3 + D*1/3
34_ = C*1/3 + D*2/3
_123 = (A*2/3 + B*1/3)*2/3 + (B*2/3 + C*1/3)*1/3 = A*4/9 + B*4/9 + C*1/9
123_ = (A*1/3 + B*2/3)*1/3 + (B*1/3 + C*2/3)*2/3 = A*1/9 + B*4/9 + C*4/9
_234 = (B*2/3 + C*1/3)*2/3 + (C*2/3 + D*1/3)*1/3 = B*4/9 + C*4/9 + D*1/9
234_ = (B*1/3 + C*2/3)*1/3 + (C*1/3 + D*2/3)*2/3 = B*1/9 + C*4/9 + D*4/9
_1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3
= A*8/27 + B*12/27 + C*6/27 + D*1/27
= E
1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3
= A*1/27 + B*6/27 + C*12/27 + D*8/27
= F
E*27 = A*8 + B*12 + C*6 + D
F*27 = A + B*6 + C*12 + D*8
Group the known values on one side:
M = E*27 - A*8 - D = B*12 + C* 6
N = F*27 - A - D*8 = B* 6 + C*12
M*2 - N = B*18
N*2 - M = C*18
B = (M*2 - N)/18
C = (N*2 - M)/18
*/
static double interp_cubic_coords(const double* src, double t)
{
double ab = interp(src[0], src[2], t);
double bc = interp(src[2], src[4], t);
double cd = interp(src[4], src[6], t);
double abc = interp(ab, bc, t);
double bcd = interp(bc, cd, t);
double abcd = interp(abc, bcd, t);
return abcd;
}
void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst) {
if (t1 == 0 && t2 == 1) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
return;
}
double ax = dst[0].x = interp_cubic_coords(&src[0].x, t1);
double ay = dst[0].y = interp_cubic_coords(&src[0].y, t1);
double ex = interp_cubic_coords(&src[0].x, (t1*2+t2)/3);
double ey = interp_cubic_coords(&src[0].y, (t1*2+t2)/3);
double fx = interp_cubic_coords(&src[0].x, (t1+t2*2)/3);
double fy = interp_cubic_coords(&src[0].y, (t1+t2*2)/3);
double dx = dst[3].x = interp_cubic_coords(&src[0].x, t2);
double dy = dst[3].y = interp_cubic_coords(&src[0].y, t2);
double mx = ex * 27 - ax * 8 - dx;
double my = ey * 27 - ay * 8 - dy;
double nx = fx * 27 - ax - dx * 8;
double ny = fy * 27 - ay - dy * 8;
/* bx = */ dst[1].x = (mx * 2 - nx) / 18;
/* by = */ dst[1].y = (my * 2 - ny) / 18;
/* cx = */ dst[2].x = (nx * 2 - mx) / 18;
/* cy = */ dst[2].y = (ny * 2 - my) / 18;
}
void sub_divide(const Cubic& src, const _Point& a, const _Point& d,
double t1, double t2, _Point dst[2]) {
double ex = interp_cubic_coords(&src[0].x, (t1 * 2 + t2) / 3);
double ey = interp_cubic_coords(&src[0].y, (t1 * 2 + t2) / 3);
double fx = interp_cubic_coords(&src[0].x, (t1 + t2 * 2) / 3);
double fy = interp_cubic_coords(&src[0].y, (t1 + t2 * 2) / 3);
double mx = ex * 27 - a.x * 8 - d.x;
double my = ey * 27 - a.y * 8 - d.y;
double nx = fx * 27 - a.x - d.x * 8;
double ny = fy * 27 - a.y - d.y * 8;
/* bx = */ dst[0].x = (mx * 2 - nx) / 18;
/* by = */ dst[0].y = (my * 2 - ny) / 18;
/* cx = */ dst[1].x = (nx * 2 - mx) / 18;
/* cy = */ dst[1].y = (ny * 2 - my) / 18;
}
/* classic one t subdivision */
static void interp_cubic_coords(const double* src, double* dst, double t)
{
double ab = interp(src[0], src[2], t);
double bc = interp(src[2], src[4], t);
double cd = interp(src[4], src[6], t);
double abc = interp(ab, bc, t);
double bcd = interp(bc, cd, t);
double abcd = interp(abc, bcd, t);
dst[0] = src[0];
dst[2] = ab;
dst[4] = abc;
dst[6] = abcd;
dst[8] = bcd;
dst[10] = cd;
dst[12] = src[6];
}
void chop_at(const Cubic& src, CubicPair& dst, double t)
{
if (t == 0.5) {
dst.pts[0] = src[0];
dst.pts[1].x = (src[0].x + src[1].x) / 2;
dst.pts[1].y = (src[0].y + src[1].y) / 2;
dst.pts[2].x = (src[0].x + 2 * src[1].x + src[2].x) / 4;
dst.pts[2].y = (src[0].y + 2 * src[1].y + src[2].y) / 4;
dst.pts[3].x = (src[0].x + 3 * (src[1].x + src[2].x) + src[3].x) / 8;
dst.pts[3].y = (src[0].y + 3 * (src[1].y + src[2].y) + src[3].y) / 8;
dst.pts[4].x = (src[1].x + 2 * src[2].x + src[3].x) / 4;
dst.pts[4].y = (src[1].y + 2 * src[2].y + src[3].y) / 4;
dst.pts[5].x = (src[2].x + src[3].x) / 2;
dst.pts[5].y = (src[2].y + src[3].y) / 2;
dst.pts[6] = src[3];
return;
}
interp_cubic_coords(&src[0].x, &dst.pts[0].x, t);
interp_cubic_coords(&src[0].y, &dst.pts[0].y, t);
}

View File

@ -1,217 +0,0 @@
/*
http://stackoverflow.com/questions/2009160/how-do-i-convert-the-2-control-points-of-a-cubic-curve-to-the-single-control-poi
*/
/*
Let's call the control points of the cubic Q0..Q3 and the control points of the quadratic P0..P2.
Then for degree elevation, the equations are:
Q0 = P0
Q1 = 1/3 P0 + 2/3 P1
Q2 = 2/3 P1 + 1/3 P2
Q3 = P2
In your case you have Q0..Q3 and you're solving for P0..P2. There are two ways to compute P1 from
the equations above:
P1 = 3/2 Q1 - 1/2 Q0
P1 = 3/2 Q2 - 1/2 Q3
If this is a degree-elevated cubic, then both equations will give the same answer for P1. Since
it's likely not, your best bet is to average them. So,
P1 = -1/4 Q0 + 3/4 Q1 + 3/4 Q2 - 1/4 Q3
Cubic defined by: P1/2 - anchor points, C1/C2 control points
|x| is the euclidean norm of x
mid-point approx of cubic: a quad that shares the same anchors with the cubic and has the
control point at C = (3·C2 - P2 + 3·C1 - P1)/4
Algorithm
pick an absolute precision (prec)
Compute the Tdiv as the root of (cubic) equation
sqrt(3)/18 · |P2 - 3·C2 + 3·C1 - P1|/2 · Tdiv ^ 3 = prec
if Tdiv < 0.5 divide the cubic at Tdiv. First segment [0..Tdiv] can be approximated with by a
quadratic, with a defect less than prec, by the mid-point approximation.
Repeat from step 2 with the second resulted segment (corresponding to 1-Tdiv)
0.5<=Tdiv<1 - simply divide the cubic in two. The two halves can be approximated by the mid-point
approximation
Tdiv>=1 - the entire cubic can be approximated by the mid-point approximation
confirmed by (maybe stolen from)
http://www.caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
// maybe in turn derived from http://www.cccg.ca/proceedings/2004/36.pdf
// also stored at http://www.cis.usouthal.edu/~hain/general/Publications/Bezier/bezier%20cccg04%20paper.pdf
*/
#include "CubicUtilities.h"
#include "CurveIntersection.h"
#include "LineIntersection.h"
#include "TSearch.h"
const bool AVERAGE_END_POINTS = true; // results in better fitting curves
#define USE_CUBIC_END_POINTS 1
static double calcTDiv(const Cubic& cubic, double precision, double start) {
const double adjust = sqrt(3) / 36;
Cubic sub;
const Cubic* cPtr;
if (start == 0) {
cPtr = &cubic;
} else {
// OPTIMIZE: special-case half-split ?
sub_divide(cubic, start, 1, sub);
cPtr = &sub;
}
const Cubic& c = *cPtr;
double dx = c[3].x - 3 * (c[2].x - c[1].x) - c[0].x;
double dy = c[3].y - 3 * (c[2].y - c[1].y) - c[0].y;
double dist = sqrt(dx * dx + dy * dy);
double tDiv3 = precision / (adjust * dist);
double t = cube_root(tDiv3);
if (start > 0) {
t = start + (1 - start) * t;
}
return t;
}
void demote_cubic_to_quad(const Cubic& cubic, Quadratic& quad) {
quad[0] = cubic[0];
if (AVERAGE_END_POINTS) {
const _Point fromC1 = { (3 * cubic[1].x - cubic[0].x) / 2, (3 * cubic[1].y - cubic[0].y) / 2 };
const _Point fromC2 = { (3 * cubic[2].x - cubic[3].x) / 2, (3 * cubic[2].y - cubic[3].y) / 2 };
quad[1].x = (fromC1.x + fromC2.x) / 2;
quad[1].y = (fromC1.y + fromC2.y) / 2;
} else {
lineIntersect((const _Line&) cubic[0], (const _Line&) cubic[2], quad[1]);
}
quad[2] = cubic[3];
}
int cubic_to_quadratics(const Cubic& cubic, double precision, SkTDArray<Quadratic>& quadratics) {
SkTDArray<double> ts;
cubic_to_quadratics(cubic, precision, ts);
int tsCount = ts.count();
double t1Start = 0;
int order = 0;
for (int idx = 0; idx <= tsCount; ++idx) {
double t1 = idx < tsCount ? ts[idx] : 1;
Cubic part;
sub_divide(cubic, t1Start, t1, part);
Quadratic q1;
demote_cubic_to_quad(part, q1);
Quadratic s1;
int o1 = reduceOrder(q1, s1, kReduceOrder_TreatAsFill);
if (order < o1) {
order = o1;
}
memcpy(quadratics.append(), o1 < 2 ? s1 : q1, sizeof(Quadratic));
t1Start = t1;
}
return order;
}
static bool addSimpleTs(const Cubic& cubic, double precision, SkTDArray<double>& ts) {
double tDiv = calcTDiv(cubic, precision, 0);
if (tDiv >= 1) {
return true;
}
if (tDiv >= 0.5) {
*ts.append() = 0.5;
return true;
}
return false;
}
static void addTs(const Cubic& cubic, double precision, double start, double end,
SkTDArray<double>& ts) {
double tDiv = calcTDiv(cubic, precision, 0);
double parts = ceil(1.0 / tDiv);
for (double index = 0; index < parts; ++index) {
double newT = start + (index / parts) * (end - start);
if (newT > 0 && newT < 1) {
*ts.append() = newT;
}
}
}
// flavor that returns T values only, deferring computing the quads until they are needed
// FIXME: when called from recursive intersect 2, this could take the original cubic
// and do a more precise job when calling chop at and sub divide by computing the fractional ts.
// it would still take the prechopped cubic for reduce order and find cubic inflections
void cubic_to_quadratics(const Cubic& cubic, double precision, SkTDArray<double>& ts) {
Cubic reduced;
int order = reduceOrder(cubic, reduced, kReduceOrder_QuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (order < 3) {
return;
}
double inflectT[5];
int inflections = find_cubic_inflections(cubic, inflectT);
SkASSERT(inflections <= 2);
if (!ends_are_extrema_in_x_or_y(cubic)) {
inflections += find_cubic_max_curvature(cubic, &inflectT[inflections]);
SkASSERT(inflections <= 5);
}
QSort<double>(inflectT, &inflectT[inflections - 1]);
// OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
// own subroutine?
while (inflections && approximately_less_than_zero(inflectT[0])) {
memcpy(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
}
int start = 0;
do {
int next = start + 1;
if (next >= inflections) {
break;
}
if (!approximately_equal(inflectT[start], inflectT[next])) {
++start;
continue;
}
memcpy(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
} while (true);
while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
--inflections;
}
CubicPair pair;
if (inflections == 1) {
chop_at(cubic, pair, inflectT[0]);
int orderP1 = reduceOrder(pair.first(), reduced, kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (orderP1 < 2) {
--inflections;
} else {
int orderP2 = reduceOrder(pair.second(), reduced, kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_TreatAsFill);
if (orderP2 < 2) {
--inflections;
}
}
}
if (inflections == 0 && addSimpleTs(cubic, precision, ts)) {
return;
}
if (inflections == 1) {
chop_at(cubic, pair, inflectT[0]);
addTs(pair.first(), precision, 0, inflectT[0], ts);
addTs(pair.second(), precision, inflectT[0], 1, ts);
return;
}
if (inflections > 1) {
Cubic part;
sub_divide(cubic, 0, inflectT[0], part);
addTs(part, precision, 0, inflectT[0], ts);
int last = inflections - 1;
for (int idx = 0; idx < last; ++idx) {
sub_divide(cubic, inflectT[idx], inflectT[idx + 1], part);
addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
}
sub_divide(cubic, inflectT[last], 1, part);
addTs(part, precision, inflectT[last], 1, ts);
return;
}
addTs(cubic, precision, 0, 1, ts);
}

View File

@ -1,269 +0,0 @@
#include "CubicIntersection_TestData.h"
#include "CubicUtilities.h"
#include "Intersection_Tests.h"
#include "QuadraticIntersection_TestData.h"
#include "TestUtilities.h"
#include "SkGeometry.h"
static void test(const Cubic* cubics, const char* name, int firstTest, size_t testCount) {
SkTDArray<Quadratic> quads;
for (size_t index = firstTest; index < testCount; ++index) {
const Cubic& cubic = cubics[index];
double precision = calcPrecision(cubic);
(void) cubic_to_quadratics(cubic, precision, quads);
if (quads.count() != 1) {
printf("%s [%d] cubic to quadratics failed count=%d\n", name, (int) index,
quads.count());
}
}
}
static void test(const Quadratic* quadTests, const char* name, int firstTest, size_t testCount) {
SkTDArray<Quadratic> quads;
for (size_t index = firstTest; index < testCount; ++index) {
const Quadratic& quad = quadTests[index];
Cubic cubic;
quad_to_cubic(quad, cubic);
double precision = calcPrecision(cubic);
(void) cubic_to_quadratics(cubic, precision, quads);
if (quads.count() != 1) {
printf("%s [%d] cubic to quadratics failed count=%d\n", name, (int) index,
quads.count());
}
}
}
static void testC(const Cubic* cubics, const char* name, int firstTest, size_t testCount) {
SkTDArray<Quadratic> quads;
// test if computed line end points are valid
for (size_t index = firstTest; index < testCount; ++index) {
const Cubic& cubic = cubics[index];
double precision = calcPrecision(cubic);
int order = cubic_to_quadratics(cubic, precision, quads);
SkASSERT(order != 4);
if (order < 3) {
continue;
}
if (!AlmostEqualUlps(cubic[0].x, quads[0][0].x)
|| !AlmostEqualUlps(cubic[0].y, quads[0][0].y)) {
printf("[%d] unmatched start\n", (int) index);
}
int last = quads.count() - 1;
if (!AlmostEqualUlps(cubic[3].x, quads[last][2].x)
|| !AlmostEqualUlps(cubic[3].y, quads[last][2].y)) {
printf("[%d] unmatched end\n", (int) index);
}
}
}
static void testC(const Cubic(* cubics)[2], const char* name, int firstTest, size_t testCount) {
SkTDArray<Quadratic> quads;
for (size_t index = firstTest; index < testCount; ++index) {
for (int idx2 = 0; idx2 < 2; ++idx2) {
const Cubic& cubic = cubics[index][idx2];
double precision = calcPrecision(cubic);
int order = cubic_to_quadratics(cubic, precision, quads);
SkASSERT(order != 4);
if (order < 3) {
continue;
}
if (!AlmostEqualUlps(cubic[0].x, quads[0][0].x)
|| !AlmostEqualUlps(cubic[0].y, quads[0][0].y)) {
printf("[%d][%d] unmatched start\n", (int) index, idx2);
}
int last = quads.count() - 1;
if (!AlmostEqualUlps(cubic[3].x, quads[last][2].x)
|| !AlmostEqualUlps(cubic[3].y, quads[last][2].y)) {
printf("[%d][%d] unmatched end\n", (int) index, idx2);
}
}
}
}
void CubicToQuadratics_Test() {
enum {
RunAll,
RunPointDegenerates,
RunNotPointDegenerates,
RunLines,
RunNotLines,
RunModEpsilonLines,
RunLessEpsilonLines,
RunNegEpsilonLines,
RunQuadraticLines,
RunQuadraticModLines,
RunComputedLines,
RunComputedTests,
RunNone
} run = RunAll;
int firstTestIndex = 0;
#if 0
run = RunComputedLines;
firstTestIndex = 18;
#endif
int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates ? firstTestIndex : SK_MaxS32;
int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates ? firstTestIndex : SK_MaxS32;
int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : SK_MaxS32;
int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : SK_MaxS32;
int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines ? firstTestIndex : SK_MaxS32;
int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines ? firstTestIndex : SK_MaxS32;
int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines ? firstTestIndex : SK_MaxS32;
int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : SK_MaxS32;
int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : SK_MaxS32;
int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines ? firstTestIndex : SK_MaxS32;
int firstComputedCubicsTest = run == RunAll ? 0 : run == RunComputedTests ? firstTestIndex : SK_MaxS32;
test(pointDegenerates, "pointDegenerates", firstPointDegeneratesTest, pointDegenerates_count);
test(notPointDegenerates, "notPointDegenerates", firstNotPointDegeneratesTest, notPointDegenerates_count);
test(lines, "lines", firstLinesTest, lines_count);
test(notLines, "notLines", firstNotLinesTest, notLines_count);
test(modEpsilonLines, "modEpsilonLines", firstModEpsilonTest, modEpsilonLines_count);
test(lessEpsilonLines, "lessEpsilonLines", firstLessEpsilonTest, lessEpsilonLines_count);
test(negEpsilonLines, "negEpsilonLines", firstNegEpsilonTest, negEpsilonLines_count);
test(quadraticLines, "quadraticLines", firstQuadraticLineTest, quadraticLines_count);
test(quadraticModEpsilonLines, "quadraticModEpsilonLines", firstQuadraticModLineTest,
quadraticModEpsilonLines_count);
testC(lines, "computed lines", firstComputedLinesTest, lines_count);
testC(tests, "computed tests", firstComputedCubicsTest, tests_count);
printf("%s end\n", __FUNCTION__);
}
static Cubic locals[] = {
{{0, 1}, {1.9274705288631189e-19, 1.0000000000000002}, {0.0017190297609673323, 0.99828097023903239},
{0.0053709083094631276, 0.99505672974365911}},
{{14.5975863, 41.632436}, {16.3518929, 26.2639684}, {18.5165519, 7.68775139}, {8.03767257, 89.1628526}},
{{69.7292014, 38.6877352}, {24.7648688, 23.1501713}, {84.9283191, 90.2588441}, {80.392774, 61.3533852}},
{{
60.776536520932126,
71.249307306133829
}, {
87.107894191103014,
22.377669868235323
}, {
1.4974754310666936,
68.069569937917208
}, {
45.261946574441133,
17.536076632112298
}},
};
static size_t localsCount = sizeof(locals) / sizeof(locals[0]);
#define DEBUG_CRASH 0
#define TEST_AVERAGE_END_POINTS 0 // must take const off to test
extern const bool AVERAGE_END_POINTS;
static void oneOff(size_t x) {
const Cubic& cubic = locals[x];
const SkPoint skcubic[4] = {{(float) cubic[0].x, (float) cubic[0].y},
{(float) cubic[1].x, (float) cubic[1].y}, {(float) cubic[2].x, (float) cubic[2].y},
{(float) cubic[3].x, (float) cubic[3].y}};
SkScalar skinflect[2];
int skin = SkFindCubicInflections(skcubic, skinflect);
SkDebugf("%s %d %1.9g\n", __FUNCTION__, skin, skinflect[0]);
SkTDArray<Quadratic> quads;
double precision = calcPrecision(cubic);
(void) cubic_to_quadratics(cubic, precision, quads);
SkDebugf("%s quads=%d\n", __FUNCTION__, quads.count());
}
void CubicsToQuadratics_OneOffTests() {
for (size_t x = 0; x < localsCount; ++x) {
oneOff(x);
}
}
void CubicsToQuadratics_OneOffTest() {
oneOff(0);
}
void CubicsToQuadratics_RandTest() {
srand(0);
const int arrayMax = 8;
const int sampleMax = 10;
const int tests = 1000000; // 10000000;
int quadDist[arrayMax];
bzero(quadDist, sizeof(quadDist));
Cubic samples[arrayMax][sampleMax];
int sampleCount[arrayMax];
bzero(sampleCount, sizeof(sampleCount));
for (int x = 0; x < tests; ++x) {
Cubic cubic;
for (int i = 0; i < 4; ++i) {
cubic[i].x = (double) rand() / RAND_MAX * 100;
cubic[i].y = (double) rand() / RAND_MAX * 100;
}
#if DEBUG_CRASH
char str[1024];
sprintf(str, "{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
cubic[0].x, cubic[0].y, cubic[1].x, cubic[1].y, cubic[2].x, cubic[2].y,
cubic[3].x, cubic[3].y);
#endif
SkTDArray<Quadratic> quads;
double precision = calcPrecision(cubic);
(void) cubic_to_quadratics(cubic, precision, quads);
int count = quads.count();
SkASSERT(count > 0);
SkASSERT(--count < arrayMax);
quadDist[count]++;
int sCount = sampleCount[count];
if (sCount < sampleMax) {
memcpy(samples[count][sCount], cubic, sizeof(Cubic));
sampleCount[count]++;
}
}
for (int x = 0; x < arrayMax; ++x) {
if (!quadDist[x]) {
continue;
}
SkDebugf("%d %1.9g%%\n", x + 1, (double) quadDist[x] / tests * 100);
}
SkDebugf("\n");
for (int x = 0; x < arrayMax; ++x) {
for (int y = 0; y < sampleCount[x]; ++y) {
#if TEST_AVERAGE_END_POINTS
for (int w = 0; w < 2; ++w) {
AVERAGE_END_POINTS = w;
#else
int w = 0;
#endif
SkDebugf("<div id=\"cubic%dx%d%s\">\n", x + 1, y, w ? "x" : "");
const Cubic& cubic = samples[x][y];
SkDebugf("{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
cubic[0].x, cubic[0].y, cubic[1].x, cubic[1].y, cubic[2].x, cubic[2].y,
cubic[3].x, cubic[3].y);
SkTDArray<Quadratic> quads;
double precision = calcPrecision(cubic);
(void) cubic_to_quadratics(cubic, precision, quads);
for (int z = 0; z < quads.count(); ++z) {
const Quadratic& quad = quads[z];
SkDebugf("{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
quad[0].x, quad[0].y, quad[1].x, quad[1].y, quad[2].x, quad[2].y);
}
SkDebugf("</div>\n\n");
#if TEST_AVERAGE_END_POINTS
}
#endif
}
}
SkDebugf("</div>\n\n");
SkDebugf("<script type=\"text/javascript\">\n\n");
SkDebugf("var testDivs = [\n");
for (int x = 0; x < arrayMax; ++x) {
for (int y = 0; y < sampleCount[x]; ++y) {
#if TEST_AVERAGE_END_POINTS
for (int w = 0; w < 2; ++w) {
#else
int w = 0;
#endif
SkDebugf(" cubic%dx%d%s,\n", x + 1, y, w ? "x" : "");
#if TEST_AVERAGE_END_POINTS
}
#endif
}
}
SkDebugf("\n\n\n");
SkDebugf("%s end\n", __FUNCTION__);
}

View File

@ -1,424 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CubicUtilities.h"
#include "Extrema.h"
#include "LineUtilities.h"
#include "QuadraticUtilities.h"
const int gPrecisionUnit = 256; // FIXME: arbitrary -- should try different values in test framework
// FIXME: cache keep the bounds and/or precision with the caller?
double calcPrecision(const Cubic& cubic) {
_Rect dRect;
dRect.setBounds(cubic); // OPTIMIZATION: just use setRawBounds ?
double width = dRect.right - dRect.left;
double height = dRect.bottom - dRect.top;
return (width > height ? width : height) / gPrecisionUnit;
}
#ifdef SK_DEBUG
double calcPrecision(const Cubic& cubic, double t, double scale) {
Cubic part;
sub_divide(cubic, SkTMax(0., t - scale), SkTMin(1., t + scale), part);
return calcPrecision(part);
}
#endif
bool clockwise(const Cubic& c) {
double sum = (c[0].x - c[3].x) * (c[0].y + c[3].y);
for (int idx = 0; idx < 3; ++idx){
sum += (c[idx + 1].x - c[idx].x) * (c[idx + 1].y + c[idx].y);
}
return sum <= 0;
}
void coefficients(const double* cubic, double& A, double& B, double& C, double& D) {
A = cubic[6]; // d
B = cubic[4] * 3; // 3*c
C = cubic[2] * 3; // 3*b
D = cubic[0]; // a
A -= D - C + B; // A = -a + 3*b - 3*c + d
B += 3 * D - 2 * C; // B = 3*a - 6*b + 3*c
C -= 3 * D; // C = -3*a + 3*b
}
bool controls_contained_by_ends(const Cubic& c) {
_Vector startTan = c[1] - c[0];
if (startTan.x == 0 && startTan.y == 0) {
startTan = c[2] - c[0];
}
_Vector endTan = c[2] - c[3];
if (endTan.x == 0 && endTan.y == 0) {
endTan = c[1] - c[3];
}
if (startTan.dot(endTan) >= 0) {
return false;
}
_Line startEdge = {c[0], c[0]};
startEdge[1].x -= startTan.y;
startEdge[1].y += startTan.x;
_Line endEdge = {c[3], c[3]};
endEdge[1].x -= endTan.y;
endEdge[1].y += endTan.x;
double leftStart1 = is_left(startEdge, c[1]);
if (leftStart1 * is_left(startEdge, c[2]) < 0) {
return false;
}
double leftEnd1 = is_left(endEdge, c[1]);
if (leftEnd1 * is_left(endEdge, c[2]) < 0) {
return false;
}
return leftStart1 * leftEnd1 >= 0;
}
bool ends_are_extrema_in_x_or_y(const Cubic& c) {
return (between(c[0].x, c[1].x, c[3].x) && between(c[0].x, c[2].x, c[3].x))
|| (between(c[0].y, c[1].y, c[3].y) && between(c[0].y, c[2].y, c[3].y));
}
bool monotonic_in_y(const Cubic& c) {
return between(c[0].y, c[1].y, c[3].y) && between(c[0].y, c[2].y, c[3].y);
}
bool serpentine(const Cubic& c) {
if (!controls_contained_by_ends(c)) {
return false;
}
double wiggle = (c[0].x - c[2].x) * (c[0].y + c[2].y);
for (int idx = 0; idx < 2; ++idx){
wiggle += (c[idx + 1].x - c[idx].x) * (c[idx + 1].y + c[idx].y);
}
double waggle = (c[1].x - c[3].x) * (c[1].y + c[3].y);
for (int idx = 1; idx < 3; ++idx){
waggle += (c[idx + 1].x - c[idx].x) * (c[idx + 1].y + c[idx].y);
}
return wiggle * waggle < 0;
}
// cubic roots
const double PI = 4 * atan(1);
// from SkGeometry.cpp (and Numeric Solutions, 5.6)
int cubicRootsValidT(double A, double B, double C, double D, double t[3]) {
#if 0
if (approximately_zero(A)) { // we're just a quadratic
return quadraticRootsValidT(B, C, D, t);
}
double a, b, c;
{
double invA = 1 / A;
a = B * invA;
b = C * invA;
c = D * invA;
}
double a2 = a * a;
double Q = (a2 - b * 3) / 9;
double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
double Q3 = Q * Q * Q;
double R2MinusQ3 = R * R - Q3;
double adiv3 = a / 3;
double* roots = t;
double r;
if (R2MinusQ3 < 0) // we have 3 real roots
{
double theta = acos(R / sqrt(Q3));
double neg2RootQ = -2 * sqrt(Q);
r = neg2RootQ * cos(theta / 3) - adiv3;
if (is_unit_interval(r))
*roots++ = r;
r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
if (is_unit_interval(r))
*roots++ = r;
r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
if (is_unit_interval(r))
*roots++ = r;
}
else // we have 1 real root
{
double A = fabs(R) + sqrt(R2MinusQ3);
A = cube_root(A);
if (R > 0) {
A = -A;
}
if (A != 0) {
A += Q / A;
}
r = A - adiv3;
if (is_unit_interval(r))
*roots++ = r;
}
return (int)(roots - t);
#else
double s[3];
int realRoots = cubicRootsReal(A, B, C, D, s);
int foundRoots = add_valid_ts(s, realRoots, t);
return foundRoots;
#endif
}
int cubicRootsReal(double A, double B, double C, double D, double s[3]) {
#ifdef SK_DEBUG
// create a string mathematica understands
// GDB set print repe 15 # if repeated digits is a bother
// set print elements 400 # if line doesn't fit
char str[1024];
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);
mathematica_ize(str, sizeof(str));
#if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
SkDebugf("%s\n", str);
#endif
#endif
if (approximately_zero(A)
&& approximately_zero_when_compared_to(A, B)
&& approximately_zero_when_compared_to(A, C)
&& approximately_zero_when_compared_to(A, D)) { // we're just a quadratic
return quadraticRootsReal(B, C, D, s);
}
if (approximately_zero_when_compared_to(D, A)
&& approximately_zero_when_compared_to(D, B)
&& approximately_zero_when_compared_to(D, C)) { // 0 is one root
int num = quadraticRootsReal(A, B, C, s);
for (int i = 0; i < num; ++i) {
if (approximately_zero(s[i])) {
return num;
}
}
s[num++] = 0;
return num;
}
if (approximately_zero(A + B + C + D)) { // 1 is one root
int num = quadraticRootsReal(A, A + B, -D, s);
for (int i = 0; i < num; ++i) {
if (AlmostEqualUlps(s[i], 1)) {
return num;
}
}
s[num++] = 1;
return num;
}
double a, b, c;
{
double invA = 1 / A;
a = B * invA;
b = C * invA;
c = D * invA;
}
double a2 = a * a;
double Q = (a2 - b * 3) / 9;
double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
double R2 = R * R;
double Q3 = Q * Q * Q;
double R2MinusQ3 = R2 - Q3;
double adiv3 = a / 3;
double r;
double* roots = s;
#if 0
if (approximately_zero_squared(R2MinusQ3) && AlmostEqualUlps(R2, Q3)) {
if (approximately_zero_squared(R)) {/* one triple solution */
*roots++ = -adiv3;
} else { /* one single and one double solution */
double u = cube_root(-R);
*roots++ = 2 * u - adiv3;
*roots++ = -u - adiv3;
}
}
else
#endif
if (R2MinusQ3 < 0) // we have 3 real roots
{
double theta = acos(R / sqrt(Q3));
double neg2RootQ = -2 * sqrt(Q);
r = neg2RootQ * cos(theta / 3) - adiv3;
*roots++ = r;
r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
if (!AlmostEqualUlps(s[0], r)) {
*roots++ = r;
}
r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
if (!AlmostEqualUlps(s[0], r) && (roots - s == 1 || !AlmostEqualUlps(s[1], r))) {
*roots++ = r;
}
}
else // we have 1 real root
{
double sqrtR2MinusQ3 = sqrt(R2MinusQ3);
double A = fabs(R) + sqrtR2MinusQ3;
A = cube_root(A);
if (R > 0) {
A = -A;
}
if (A != 0) {
A += Q / A;
}
r = A - adiv3;
*roots++ = r;
if (AlmostEqualUlps(R2, Q3)) {
r = -A / 2 - adiv3;
if (!AlmostEqualUlps(s[0], r)) {
*roots++ = r;
}
}
}
return (int)(roots - s);
}
// from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf
// c(t) = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3
// c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2
// = 3(b-a)(1-t)^2 + 6(c-b)t(1-t) + 3(d-c)t^2
static double derivativeAtT(const double* cubic, double t) {
double one_t = 1 - t;
double a = cubic[0];
double b = cubic[2];
double c = cubic[4];
double d = cubic[6];
return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
}
double dx_at_t(const Cubic& cubic, double t) {
return derivativeAtT(&cubic[0].x, t);
}
double dy_at_t(const Cubic& cubic, double t) {
return derivativeAtT(&cubic[0].y, t);
}
// OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
_Vector dxdy_at_t(const Cubic& cubic, double t) {
_Vector result = { derivativeAtT(&cubic[0].x, t), derivativeAtT(&cubic[0].y, t) };
return result;
}
// OPTIMIZE? share code with formulate_F1DotF2
int find_cubic_inflections(const Cubic& src, double tValues[])
{
double Ax = src[1].x - src[0].x;
double Ay = src[1].y - src[0].y;
double Bx = src[2].x - 2 * src[1].x + src[0].x;
double By = src[2].y - 2 * src[1].y + src[0].y;
double Cx = src[3].x + 3 * (src[1].x - src[2].x) - src[0].x;
double Cy = src[3].y + 3 * (src[1].y - src[2].y) - src[0].y;
return quadraticRootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues);
}
static void formulate_F1DotF2(const double src[], double coeff[4])
{
double a = src[2] - src[0];
double b = src[4] - 2 * src[2] + src[0];
double c = src[6] + 3 * (src[2] - src[4]) - src[0];
coeff[0] = c * c;
coeff[1] = 3 * b * c;
coeff[2] = 2 * b * b + c * a;
coeff[3] = a * b;
}
/* from SkGeometry.cpp
Looking for F' dot F'' == 0
A = b - a
B = c - 2b + a
C = d - 3c + 3b - a
F' = 3Ct^2 + 6Bt + 3A
F'' = 6Ct + 6B
F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
*/
int find_cubic_max_curvature(const Cubic& src, double tValues[])
{
double coeffX[4], coeffY[4];
int i;
formulate_F1DotF2(&src[0].x, coeffX);
formulate_F1DotF2(&src[0].y, coeffY);
for (i = 0; i < 4; i++) {
coeffX[i] = coeffX[i] + coeffY[i];
}
return cubicRootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues);
}
bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath) {
double dy = cubic[index].y - cubic[zero].y;
double dx = cubic[index].x - cubic[zero].x;
if (approximately_zero(dy)) {
if (approximately_zero(dx)) {
return false;
}
memcpy(rotPath, cubic, sizeof(Cubic));
return true;
}
for (int index = 0; index < 4; ++index) {
rotPath[index].x = cubic[index].x * dx + cubic[index].y * dy;
rotPath[index].y = cubic[index].y * dx - cubic[index].x * dy;
}
return true;
}
#if 0 // unused for now
double secondDerivativeAtT(const double* cubic, double t) {
double a = cubic[0];
double b = cubic[2];
double c = cubic[4];
double d = cubic[6];
return (c - 2 * b + a) * (1 - t) + (d - 2 * c + b) * t;
}
#endif
_Point top(const Cubic& cubic, double startT, double endT) {
Cubic sub;
sub_divide(cubic, startT, endT, sub);
_Point topPt = sub[0];
if (topPt.y > sub[3].y || (topPt.y == sub[3].y && topPt.x > sub[3].x)) {
topPt = sub[3];
}
double extremeTs[2];
if (!monotonic_in_y(sub)) {
int roots = findExtrema(sub[0].y, sub[1].y, sub[2].y, sub[3].y, extremeTs);
for (int index = 0; index < roots; ++index) {
_Point mid;
double t = startT + (endT - startT) * extremeTs[index];
xy_at_t(cubic, t, mid.x, mid.y);
if (topPt.y > mid.y || (topPt.y == mid.y && topPt.x > mid.x)) {
topPt = mid;
}
}
}
return topPt;
}
// OPTIMIZE: avoid computing the unused half
void xy_at_t(const Cubic& cubic, double t, double& x, double& y) {
_Point xy = xy_at_t(cubic, t);
if (&x) {
x = xy.x;
}
if (&y) {
y = xy.y;
}
}
_Point xy_at_t(const Cubic& cubic, double t) {
double one_t = 1 - t;
double one_t2 = one_t * one_t;
double a = one_t2 * one_t;
double b = 3 * one_t2 * t;
double t2 = t * t;
double c = 3 * one_t * t2;
double d = t2 * t;
_Point result = {a * cubic[0].x + b * cubic[1].x + c * cubic[2].x + d * cubic[3].x,
a * cubic[0].y + b * cubic[1].y + c * cubic[2].y + d * cubic[3].y};
return result;
}

View File

@ -1,46 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#if !defined CUBIC_UTILITIES_H
#define CUBIC_UTILITIES_H
#include "DataTypes.h"
#include "SkTDArray.h"
double calcPrecision(const Cubic& cubic);
#ifdef SK_DEBUG
double calcPrecision(const Cubic& cubic, double t, double scale);
#endif
void chop_at(const Cubic& src, CubicPair& dst, double t);
bool clockwise(const Cubic& c);
double cube_root(double x);
int cubic_to_quadratics(const Cubic& cubic, double precision,
SkTDArray<Quadratic>& quadratics);
void cubic_to_quadratics(const Cubic& cubic, double precision, SkTDArray<double>& ts);
void coefficients(const double* cubic, double& A, double& B, double& C, double& D);
bool controls_contained_by_ends(const Cubic& c);
int cubicRootsValidT(double A, double B, double C, double D, double t[3]);
int cubicRootsReal(double A, double B, double C, double D, double s[3]);
void demote_cubic_to_quad(const Cubic& cubic, Quadratic& quad);
double dx_at_t(const Cubic& , double t);
double dy_at_t(const Cubic& , double t);
//void dxdy_at_t(const Cubic& , double t, _Point& y);
_Vector dxdy_at_t(const Cubic& cubic, double t);
bool ends_are_extrema_in_x_or_y(const Cubic& );
int find_cubic_inflections(const Cubic& src, double tValues[]);
int find_cubic_max_curvature(const Cubic& src, double tValues[]);
bool monotonic_in_y(const Cubic& c);
bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath);
bool serpentine(const Cubic& c);
void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst);
void sub_divide(const Cubic& , const _Point& a, const _Point& d, double t1, double t2, _Point [2]);
_Point top(const Cubic& , double startT, double endT);
void xy_at_t(const Cubic& , double t, double& x, double& y);
_Point xy_at_t(const Cubic& , double t);
extern const int gPrecisionUnit;
#endif

View File

@ -1,28 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Intersection_Tests.h"
#include "CubicUtilities.h"
const Cubic tests[] = {
{{2, 0}, {3, 1}, {2, 2}, {1, 1}},
{{3, 1}, {2, 2}, {1, 1}, {2, 0}},
{{3, 0}, {2, 1}, {3, 2}, {1, 1}},
};
const size_t tests_count = sizeof(tests) / sizeof(tests[0]);
static size_t firstLineParameterTest = 0;
void CubicUtilities_Test() {
for (size_t index = firstLineParameterTest; index < tests_count; ++index) {
const Cubic& cubic = tests[index];
bool result = clockwise(cubic);
if (!result) {
SkDebugf("%s [%d] expected clockwise\n", __FUNCTION__, index);
SkASSERT(0);
}
}
}

View File

@ -1,80 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef CurveIntersection_DEFINE
#define CurveIntersection_DEFINE
#include "DataTypes.h"
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);
int convex_hull(const Cubic& cubic, char order[4]);
bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]);
bool implicit_matches(const Cubic& cubic1, const Cubic& cubic2);
bool implicit_matches(const _Line& line1, const _Line& line2);
bool implicit_matches_ulps(const _Line& one, const _Line& two, int ulps);
bool implicit_matches(const Quadratic& quad1, const Quadratic& quad2);
void tangent(const Cubic& cubic, double t, _Point& result);
void tangent(const _Line& line, _Point& result);
void tangent(const Quadratic& quad, double t, _Point& result);
// main functions
enum ReduceOrder_Quadratics {
kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_QuadraticsAllowed
};
enum ReduceOrder_Styles {
kReduceOrder_TreatAsStroke,
kReduceOrder_TreatAsFill
};
int reduceOrder(const Cubic& cubic, Cubic& reduction, ReduceOrder_Quadratics ,
ReduceOrder_Styles );
int reduceOrder(const _Line& line, _Line& reduction);
int reduceOrder(const Quadratic& quad, Quadratic& reduction, ReduceOrder_Styles );
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& );
// the following flavor uses quadratic approximation instead of convex hulls
//bool intersect2(const Cubic& cubic1, const Cubic& cubic2, Intersections& );
// like '2', but iterates on centers instead of possible edges
bool intersect3(const Cubic& cubic1, const Cubic& cubic2, Intersections& );
int intersect(const Cubic& cubic, Intersections& i); // return true if cubic self-intersects
int intersect(const Cubic& cubic, const Quadratic& quad, Intersections& );
int intersect(const Cubic& cubic, const _Line& line, Intersections& );
int intersectRay(const Cubic& quad, const _Line& line, Intersections& i);
bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& );
int intersect(const Quadratic& quad, const _Line& line, Intersections& );
// the following flavor uses the implicit form instead of convex hulls
bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i);
int intersectRay(const Quadratic& quad, const _Line& line, Intersections& i);
bool isLinear(const Quadratic& quad, int startIndex, int endIndex);
bool isLinear(const Cubic& cubic, int startIndex, int endIndex);
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

View File

@ -1,15 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef CurveUtilities_DEFINE
#define CurveUtilities_DEFINE
#include "CubicUtilities.h"
#include "LineUtilities.h"
#include "QuadraticUtilities.h"
#endif

View File

@ -1,108 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
#include <sys/types.h>
#include <stdlib.h>
#if USE_EPSILON
const double PointEpsilon = 0.000001;
const double SquaredEpsilon = PointEpsilon * PointEpsilon;
#endif
const int UlpsEpsilon = 16;
_Vector operator-(const _Point& a, const _Point& b) {
_Vector v = {a.x - b.x, a.y - b.y};
return v;
}
_Point operator+(const _Point& a, const _Vector& b) {
_Point v = {a.x + b.x, a.y + b.y};
return v;
}
// from http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
union Float_t
{
Float_t(float num = 0.0f) : f(num) {}
// Portable extraction of components.
bool negative() const { return (i >> 31) != 0; }
#if 0 // unused
int32_t RawMantissa() const { return i & ((1 << 23) - 1); }
int32_t RawExponent() const { return (i >> 23) & 0xFF; }
#endif
int32_t i;
float f;
#ifdef SK_DEBUG
struct
{ // Bitfields for exploration. Do not use in production code.
uint32_t mantissa : 23;
uint32_t exponent : 8;
uint32_t sign : 1;
} parts;
#endif
};
bool AlmostEqualUlps(float A, float B)
{
Float_t uA(A);
Float_t uB(B);
// Different signs means they do not match.
if (uA.negative() != uB.negative())
{
// Check for equality to make sure +0==-0
return A == B;
}
// Find the difference in ULPs.
int ulpsDiff = abs(uA.i - uB.i);
return ulpsDiff <= UlpsEpsilon;
}
// FIXME: obsolete, delete
#if 1
int UlpsDiff(float A, float B)
{
Float_t uA(A);
Float_t uB(B);
return abs(uA.i - uB.i);
}
#endif
#ifdef 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';
}
}
bool valid_wind(int wind) {
return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
}
void winding_printf(int wind) {
if (wind == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", wind);
}
}
#endif

View File

@ -1,418 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef __DataTypes_h__
#define __DataTypes_h__
#include <float.h> // for FLT_EPSILON
#include <math.h> // for fabs, sqrt
#include "SkPoint.h"
#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging
#define ONE_OFF_DEBUG 1
#define ONE_OFF_DEBUG_MATHEMATICA 0
// FIXME: move these into SkTypes.h
template <typename T> inline T SkTMax(T a, T b) {
if (a < b)
a = b;
return a;
}
template <typename T> inline T SkTMin(T a, T b) {
if (a > b)
a = b;
return a;
}
extern bool AlmostEqualUlps(float A, float B);
inline bool AlmostEqualUlps(double A, double B) { return AlmostEqualUlps((float) A, (float) B); }
// FIXME: delete
int UlpsDiff(float A, float B);
// FLT_EPSILON == 1.19209290E-07 == 1 / (2 ^ 23)
// DBL_EPSILON == 2.22045e-16
const double FLT_EPSILON_CUBED = FLT_EPSILON * FLT_EPSILON * FLT_EPSILON;
const double FLT_EPSILON_HALF = FLT_EPSILON / 2;
const double FLT_EPSILON_SQUARED = FLT_EPSILON * FLT_EPSILON;
const double FLT_EPSILON_SQRT = sqrt(FLT_EPSILON);
const double FLT_EPSILON_INVERSE = 1 / FLT_EPSILON;
const double DBL_EPSILON_ERR = DBL_EPSILON * 4; // tune -- allow a few bits of error
const double ROUGH_EPSILON = FLT_EPSILON * 64;
const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256;
inline bool approximately_zero(double x) {
return fabs(x) < FLT_EPSILON;
}
inline bool precisely_zero(double x) {
return fabs(x) < DBL_EPSILON_ERR;
}
inline bool approximately_zero(float x) {
return fabs(x) < FLT_EPSILON;
}
inline bool approximately_zero_cubed(double x) {
return fabs(x) < FLT_EPSILON_CUBED;
}
inline bool approximately_zero_half(double x) {
return fabs(x) < FLT_EPSILON_HALF;
}
inline bool approximately_zero_squared(double x) {
return fabs(x) < FLT_EPSILON_SQUARED;
}
inline bool approximately_zero_sqrt(double x) {
return fabs(x) < FLT_EPSILON_SQRT;
}
inline bool approximately_zero_inverse(double x) {
return fabs(x) > FLT_EPSILON_INVERSE;
}
// FIXME: if called multiple times with the same denom, we want to pass 1/y instead
inline bool approximately_zero_when_compared_to(double x, double y) {
return x == 0 || fabs(x / y) < FLT_EPSILON;
}
// Use this for comparing Ts in the range of 0 to 1. For general numbers (larger and smaller) use
// AlmostEqualUlps instead.
inline bool approximately_equal(double x, double y) {
#if 1
return approximately_zero(x - y);
#else
// see http://visualstudiomagazine.com/blogs/tool-tracker/2011/11/compare-floating-point-numbers.aspx
// this allows very small (e.g. degenerate) values to compare unequally, but in this case,
// AlmostEqualUlps should be used instead.
if (x == y) {
return true;
}
double absY = fabs(y);
if (x == 0) {
return absY < FLT_EPSILON;
}
double absX = fabs(x);
if (y == 0) {
return absX < FLT_EPSILON;
}
return fabs(x - y) < (absX > absY ? absX : absY) * FLT_EPSILON;
#endif
}
inline bool precisely_equal(double x, double y) {
return precisely_zero(x - y);
}
inline bool approximately_equal_half(double x, double y) {
return approximately_zero_half(x - y);
}
inline bool approximately_equal_squared(double x, double y) {
return approximately_equal(x, y);
}
inline bool approximately_greater(double x, double 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) {
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) {
return approximately_zero(x) ? 0 : x;
}
inline float approximately_pin(float x) {
return approximately_zero(x) ? 0 : x;
}
inline bool approximately_greater_than_one(double x) {
return x > 1 - FLT_EPSILON;
}
inline bool precisely_greater_than_one(double x) {
return x > 1 - DBL_EPSILON_ERR;
}
inline bool approximately_less_than_zero(double x) {
return x < FLT_EPSILON;
}
inline bool precisely_less_than_zero(double x) {
return x < DBL_EPSILON_ERR;
}
inline bool approximately_negative(double x) {
return x < FLT_EPSILON;
}
inline bool precisely_negative(double x) {
return x < DBL_EPSILON_ERR;
}
inline bool approximately_one_or_less(double x) {
return x < 1 + FLT_EPSILON;
}
inline bool approximately_positive(double x) {
return x > -FLT_EPSILON;
}
inline bool approximately_positive_squared(double x) {
return x > -(FLT_EPSILON_SQUARED);
}
inline bool approximately_zero_or_more(double x) {
return x > -FLT_EPSILON;
}
inline bool approximately_between(double a, double b, double c) {
return a <= c ? approximately_negative(a - b) && approximately_negative(b - c)
: approximately_negative(b - a) && approximately_negative(c - b);
}
// returns true if (a <= b <= c) || (a >= b >= c)
inline bool between(double a, double b, double c) {
SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0));
return (a - b) * (c - b) <= 0;
}
inline bool more_roughly_equal(double x, double y) {
return fabs(x - y) < MORE_ROUGH_EPSILON;
}
inline bool roughly_equal(double x, double y) {
return fabs(x - y) < ROUGH_EPSILON;
}
struct _Point;
struct _Vector {
double x;
double y;
friend _Point operator+(const _Point& a, const _Vector& b);
void operator+=(const _Vector& v) {
x += v.x;
y += v.y;
}
void operator-=(const _Vector& v) {
x -= v.x;
y -= v.y;
}
void operator/=(const double s) {
x /= s;
y /= s;
}
void operator*=(const double s) {
x *= s;
y *= s;
}
double cross(const _Vector& a) const {
return x * a.y - y * a.x;
}
double dot(const _Vector& a) const {
return x * a.x + y * a.y;
}
double length() const {
return sqrt(lengthSquared());
}
double lengthSquared() const {
return x * x + y * y;
}
SkVector asSkVector() const {
SkVector v = {SkDoubleToScalar(x), SkDoubleToScalar(y)};
return v;
}
};
struct _Point {
double x;
double y;
friend _Vector operator-(const _Point& a, const _Point& b);
void operator+=(const _Vector& v) {
x += v.x;
y += v.y;
}
void operator-=(const _Vector& v) {
x -= v.x;
y -= v.y;
}
friend bool operator==(const _Point& a, const _Point& b) {
return a.x == b.x && a.y == b.y;
}
friend bool operator!=(const _Point& a, const _Point& b) {
return a.x != b.x || a.y != b.y;
}
// note: this can not be implemented with
// return approximately_equal(a.y, y) && approximately_equal(a.x, x);
// because that will not take the magnitude of the values
bool approximatelyEqual(const _Point& a) const {
double denom = SkTMax(fabs(x), SkTMax(fabs(y), SkTMax(fabs(a.x), fabs(a.y))));
if (denom == 0) {
return true;
}
double inv = 1 / denom;
return approximately_equal(x * inv, a.x * inv) && approximately_equal(y * inv, a.y * inv);
}
bool approximatelyEqual(const SkPoint& a) const {
double denom = SkTMax(fabs(x), SkTMax(fabs(y), SkTMax(fabs(a.fX), fabs(a.fY))));
if (denom == 0) {
return true;
}
double inv = 1 / denom;
return approximately_equal(x * inv, a.fX * inv) && approximately_equal(y * inv, a.fY * inv);
}
bool approximatelyEqualHalf(const _Point& a) const {
double denom = SkTMax(fabs(x), SkTMax(fabs(y), SkTMax(fabs(a.x), fabs(a.y))));
if (denom == 0) {
return true;
}
double inv = 1 / denom;
return approximately_equal_half(x * inv, a.x * inv)
&& approximately_equal_half(y * inv, a.y * inv);
}
bool approximatelyZero() const {
return approximately_zero(x) && approximately_zero(y);
}
SkPoint asSkPoint() const {
SkPoint pt = {SkDoubleToScalar(x), SkDoubleToScalar(y)};
return pt;
}
double distance(const _Point& a) const {
_Vector temp = *this - a;
return temp.length();
}
double distanceSquared(const _Point& a) const {
_Vector temp = *this - a;
return temp.lengthSquared();
}
double moreRoughlyEqual(const _Point& a) const {
return more_roughly_equal(a.y, y) && more_roughly_equal(a.x, x);
}
double roughlyEqual(const _Point& a) const {
return roughly_equal(a.y, y) && roughly_equal(a.x, x);
}
};
typedef _Point _Line[2];
typedef _Point Quadratic[3];
typedef _Point Triangle[3];
typedef _Point Cubic[4];
struct _Rect {
double left;
double top;
double right;
double bottom;
void add(const _Point& pt) {
if (left > pt.x) {
left = pt.x;
}
if (top > pt.y) {
top = pt.y;
}
if (right < pt.x) {
right = pt.x;
}
if (bottom < pt.y) {
bottom = pt.y;
}
}
// FIXME: used by debugging only ?
bool contains(const _Point& pt) const {
return approximately_between(left, pt.x, right)
&& approximately_between(top, pt.y, bottom);
}
bool intersects(_Rect& r) const {
SkASSERT(left <= right);
SkASSERT(top <= bottom);
SkASSERT(r.left <= r.right);
SkASSERT(r.top <= r.bottom);
return r.left <= right && left <= r.right && r.top <= bottom && top <= r.bottom;
}
void set(const _Point& pt) {
left = right = pt.x;
top = bottom = pt.y;
}
void setBounds(const _Line& line) {
set(line[0]);
add(line[1]);
}
void setBounds(const Cubic& );
void setBounds(const Quadratic& );
void setRawBounds(const Cubic& );
void setRawBounds(const Quadratic& );
};
struct CubicPair {
const Cubic& first() const { return (const Cubic&) pts[0]; }
const Cubic& second() const { return (const Cubic&) pts[3]; }
_Point pts[7];
};
struct QuadraticPair {
const Quadratic& first() const { return (const Quadratic&) pts[0]; }
const Quadratic& second() const { return (const Quadratic&) pts[2]; }
_Point pts[5];
};
// FIXME: move these into SkFloatingPoint.h
#include "SkFloatingPoint.h"
#define sk_double_isnan(a) sk_float_isnan(a)
// FIXME: move these to debugging file
#ifdef SK_DEBUG
void mathematica_ize(char* str, size_t bufferSize);
bool valid_wind(int winding);
void winding_printf(int winding);
#endif
#endif // __DataTypes_h__

View File

@ -1,7 +0,0 @@
#ifndef __DataTypes_Test_h__
#define __DataTypes_Test_h__
const double PointEpsilon = 0.000001;
const double SquaredEpsilon = PointEpsilon * PointEpsilon;
#endif

View File

@ -1,347 +0,0 @@
#include "EdgeDemo.h"
#include "EdgeWalker_Test.h"
#include "ShapeOps.h"
#import "SkCanvas.h"
#import "SkPaint.h"
extern void showPath(const SkPath& path, const char* str);
static bool drawPaths(SkCanvas* canvas, const SkPath& path, bool useOld)
{
SkPath out;
#define SHOW_PATH 0
#if SHOW_PATH
showPath(path, "original:");
#endif
if (useOld) {
simplify(path, true, out);
} else {
simplifyx(path, out);
}
#if SHOW_PATH
showPath(out, "simplified:");
#endif
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
// paint.setStrokeWidth(6);
// paint.setColor(0x1F003f7f);
// canvas->drawPath(path, paint);
paint.setColor(0xFF305F00);
paint.setStrokeWidth(1);
canvas->drawPath(out, paint);
return true;
}
// Three circles bounce inside a rectangle. The circles describe three, four
// or five points which in turn describe a polygon. The polygon points
// bounce inside the circles. The circles rotate and scale over time. The
// polygons are combined into a single path, simplified, and stroked.
static bool drawCircles(SkCanvas* canvas, int step, bool useOld)
{
const int circles = 3;
int scales[circles];
int angles[circles];
int locs[circles * 2];
int pts[circles * 2 * 4];
int c, p;
for (c = 0; c < circles; ++c) {
scales[c] = abs(10 - (step + c * 4) % 21);
angles[c] = (step + c * 6) % 600;
locs[c * 2] = abs(130 - (step + c * 9) % 261);
locs[c * 2 + 1] = abs(170 - (step + c * 11) % 341);
for (p = 0; p < 4; ++p) {
pts[c * 8 + p * 2] = abs(90 - ((step + c * 121 + p * 13) % 190));
pts[c * 8 + p * 2 + 1] = abs(110 - ((step + c * 223 + p * 17) % 230));
}
}
SkPath path;
for (c = 0; c < circles; ++c) {
for (p = 0; p < 4; ++p) {
SkScalar x = pts[c * 8 + p * 2];
SkScalar y = pts[c * 8 + p * 2 + 1];
x *= 3 + scales[c] / 10.0f;
y *= 3 + scales[c] / 10.0f;
SkScalar angle = angles[c] * 3.1415f * 2 / 600;
SkScalar temp = (SkScalar) (x * cos(angle) - y * sin(angle));
y = (SkScalar) (x * sin(angle) + y * cos(angle));
x = temp;
x += locs[c * 2] * 200 / 130.0f;
y += locs[c * 2 + 1] * 200 / 170.0f;
x += 50;
// y += 200;
if (p == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.close();
}
return drawPaths(canvas, path, useOld);
}
static void createStar(SkPath& path, SkScalar innerRadius, SkScalar outerRadius,
SkScalar startAngle, int points, SkPoint center) {
SkScalar angle = startAngle;
for (int index = 0; index < points * 2; ++index) {
SkScalar radius = index & 1 ? outerRadius : innerRadius;
SkScalar x = (SkScalar) (radius * cos(angle));
SkScalar y = (SkScalar) (radius * sin(angle));
x += center.fX;
y += center.fY;
if (index == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
angle += 3.1415f / points;
}
path.close();
}
static bool drawStars(SkCanvas* canvas, int step, bool useOld)
{
SkPath path;
const int stars = 25;
int pts[stars];
// static bool initialize = true;
int s;
for (s = 0; s < stars; ++s) {
pts[s] = 4 + (s % 7);
}
SkPoint locs[stars];
SkScalar angles[stars];
SkScalar innerRadius[stars];
SkScalar outerRadius[stars];
const int width = 640;
const int height = 480;
const int margin = 30;
const int minRadius = 120;
const int maxInner = 800;
const int maxOuter = 1153;
for (s = 0; s < stars; ++s) {
int starW = (int) (width - margin * 2 + (SkScalar) s * (stars - s) / stars);
locs[s].fX = (int) (step * (1.3f * (s + 1) / stars) + s * 121) % (starW * 2);
if (locs[s].fX > starW) {
locs[s].fX = starW * 2 - locs[s].fX;
}
locs[s].fX += margin;
int starH = (int) (height - margin * 2 + (SkScalar) s * s / stars);
locs[s].fY = (int) (step * (1.7f * (s + 1) / stars) + s * 183) % (starH * 2);
if (locs[s].fY > starH) {
locs[s].fY = starH * 2 - locs[s].fY;
}
locs[s].fY += margin;
angles[s] = ((step + s * 47) % (360 * 4)) * 3.1415f / 180 / 4;
innerRadius[s] = (step + s * 30) % (maxInner * 2);
if (innerRadius[s] > maxInner) {
innerRadius[s] = (maxInner * 2) - innerRadius[s];
}
innerRadius[s] = innerRadius[s] / 4 + minRadius;
outerRadius[s] = (step + s * 70) % (maxOuter * 2);
if (outerRadius[s] > maxOuter) {
outerRadius[s] = (maxOuter * 2) - outerRadius[s];
}
outerRadius[s] = outerRadius[s] / 4 + minRadius;
createStar(path, innerRadius[s] / 4.0f, outerRadius[s] / 4.0f,
angles[s], pts[s], locs[s]);
}
return drawPaths(canvas, path, useOld);
}
#if 0
static void tryRoncoOnce(const SkPath& path, const SkRect& target, bool show) {
// capture everything in a desired rectangle
SkPath tiny;
bool closed = true;
SkPath::Iter iter(path, false);
SkPoint pts[4];
SkPath::Verb verb;
int count = 0;
SkPoint lastPt;
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
count = 0;
break;
case SkPath::kLine_Verb:
count = 1;
break;
case SkPath::kQuad_Verb:
count = 2;
break;
case SkPath::kCubic_Verb:
count = 3;
break;
case SkPath::kClose_Verb:
if (!closed) {
tiny.close();
closed = true;
}
count = 0;
break;
default:
SkDEBUGFAIL("bad verb");
}
if (!count) {
continue;
}
SkRect bounds;
bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
for (int i = 1; i <= count; ++i) {
bounds.growToInclude(pts[i].fX + 0.1f, pts[i].fY + 0.1f);
}
if (!SkRect::Intersects(target, bounds)) {
continue;
}
if (closed) {
tiny.moveTo(pts[0].fX, pts[0].fY);
closed = false;
} else if (pts[0] != lastPt) {
tiny.lineTo(pts[0].fX, pts[0].fY);
}
switch (verb) {
case SkPath::kLine_Verb:
tiny.lineTo(pts[1].fX, pts[1].fY);
lastPt = pts[1];
break;
case SkPath::kQuad_Verb:
tiny.quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
lastPt = pts[2];
break;
case SkPath::kCubic_Verb:
tiny.cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
lastPt = pts[3];
break;
default:
SkDEBUGFAIL("bad verb");
}
}
if (!closed) {
tiny.close();
}
if (show) {
showPath(tiny, NULL);
SkDebugf("simplified:\n");
}
testSimplifyx(tiny);
}
#endif
#if 0
static void tryRonco(const SkPath& path) {
int divMax = 64;
int divMin = 1;
int xDivMin = 0;
int yDivMin = 0;
bool allYs = true;
bool allXs = true;
if (1) {
divMax = divMin = 64;
xDivMin = 11;
yDivMin = 0;
allXs = true;
allYs = true;
}
for (int divs = divMax; divs >= divMin; divs /= 2) {
SkDebugf("divs=%d\n",divs);
const SkRect& overall = path.getBounds();
SkScalar cellWidth = overall.width() / divs * 2;
SkScalar cellHeight = overall.height() / divs * 2;
SkRect target;
int xDivMax = divMax == divMin && !allXs ? xDivMin + 1 : divs;
int yDivMax = divMax == divMin && !allYs ? yDivMin + 1 : divs;
for (int xDiv = xDivMin; xDiv < xDivMax; ++xDiv) {
SkDebugf("xDiv=%d\n",xDiv);
for (int yDiv = yDivMin; yDiv < yDivMax; ++yDiv) {
SkDebugf("yDiv=%d\n",yDiv);
target.setXYWH(overall.fLeft + (overall.width() - cellWidth) * xDiv / divs,
overall.fTop + (overall.height() - cellHeight) * yDiv / divs,
cellWidth, cellHeight);
tryRoncoOnce(path, target, divMax == divMin);
}
}
}
}
#endif
static bool drawLetters(SkCanvas* canvas, int step, bool useOld)
{
SkPath path;
const int width = 640;
const int height = 480;
const char testStr[] = "Merge";
const int testStrLen = sizeof(testStr) - 1;
SkPoint textPos[testStrLen];
SkScalar widths[testStrLen];
SkPaint paint;
paint.setTextSize(40);
paint.setAntiAlias(true);
paint.getTextWidths(testStr, testStrLen, widths, NULL);
SkScalar running = 0;
for (int x = 0; x < testStrLen; ++x) {
SkScalar width = widths[x];
widths[x] = running;
running += width;
}
SkScalar bias = (width - widths[testStrLen - 1]) / 2;
for (int x = 0; x < testStrLen; ++x) {
textPos[x].fX = bias + widths[x];
textPos[x].fY = height / 2;
}
paint.setTextSize(40 + step / 100.0f);
#if 0
bool oneShot = false;
for (int mask = 0; mask < 1 << testStrLen; ++mask) {
char maskStr[testStrLen];
#if 1
mask = 12;
oneShot = true;
#endif
SkDebugf("mask=%d\n", mask);
for (int letter = 0; letter < testStrLen; ++letter) {
maskStr[letter] = mask & (1 << letter) ? testStr[letter] : ' ';
}
paint.getPosTextPath(maskStr, testStrLen, textPos, &path);
// showPath(path, NULL);
// SkDebugf("%d simplified:\n", mask);
tryRonco(path);
// testSimplifyx(path);
if (oneShot) {
break;
}
}
#endif
paint.getPosTextPath(testStr, testStrLen, textPos, &path);
#if 0
tryRonco(path);
SkDebugf("RoncoDone!\n");
#endif
#if 0
showPath(path, NULL);
SkDebugf("simplified:\n");
#endif
return drawPaths(canvas, path, false);
}
static bool (*drawDemos[])(SkCanvas* , int , bool ) = {
drawStars,
drawCircles,
drawLetters,
};
static size_t drawDemosCount = sizeof(drawDemos) / sizeof(drawDemos[0]);
static bool (*firstTest)(SkCanvas* , int , bool) = drawStars;
bool DrawEdgeDemo(SkCanvas* canvas, int step, bool useOld) {
size_t index = 0;
if (firstTest) {
while (index < drawDemosCount && drawDemos[index] != firstTest) {
++index;
}
}
return (*drawDemos[index])(canvas, step, useOld);
}

View File

@ -1,3 +0,0 @@
class SkCanvas;
bool DrawEdgeDemo(SkCanvas* canvas, int step, bool useOld);

View File

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

View File

@ -1,94 +0,0 @@
#include "EdgeDemo.h"
#import "SkCanvas.h"
#import "SkWindow.h"
#include "SkGraphics.h"
#include "SkCGUtils.h"
#include <time.h>
#include <sys/time.h>
class SkSampleView : public SkView {
public:
SkSampleView() {
this->setVisibleP(true);
this->setClipToBounds(false);
useOld = false;
};
protected:
virtual void onDraw(SkCanvas* canvas) {
static int step = 0; // 17907 drawLetters first error
// drawStars triggers error at 33348
// drawStars error not easy to debug last time I checked
static double seconds;
if (step == -1) {
timeval t;
gettimeofday(&t, NULL);
seconds = t.tv_sec+t.tv_usec/1000000.0;
step = 0;
}
canvas->drawColor(SK_ColorWHITE);
if (DrawEdgeDemo(canvas, step, useOld)) {
++step;
if (step == -1) {
timeval t;
gettimeofday(&t, NULL);
double last = seconds;
seconds = t.tv_sec+t.tv_usec/1000000.0;
SkDebugf("old=%d seconds=%g\n", useOld, seconds - last);
useOld ^= true;
step = 0;
}
inval(NULL);
}
}
virtual Click* onFindClickHandler(SkScalar , SkScalar ) {
useOld ^= true;
return NULL;
}
private:
bool useOld;
typedef SkView INHERITED;
};
void application_init();
void application_term();
void application_init() {
SkGraphics::Init();
SkEvent::Init();
}
void application_term() {
SkGraphics::Term();
SkEvent::Term();
}
class FillLayout : public SkView::Layout {
protected:
virtual void onLayoutChildren(SkView* parent) {
SkView* view = SkView::F2BIter(parent).next();
view->setSize(parent->width(), parent->height());
}
};
#import "SimpleApp.h"
@implementation SimpleNSView
- (id)initWithDefaults {
if ((self = [super initWithDefaults])) {
fWind = new SkOSWindow(self);
fWind->setLayout(new FillLayout, false);
fWind->attachChildToFront(new SkSampleView)->unref();
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
SkCGDrawBitmap(ctx, fWind->getBitmap(), 0, 0);
}
@end

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Intersection_Tests.h"
int main(int /*argc*/, char** /*argv*/) {
Intersection_Tests();
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,290 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
static void* testSimplify4x4QuadralateralsMain(void* data)
{
SkASSERT(data);
State4& state = *(State4*) data;
char pathStr[1024];
bzero(pathStr, sizeof(pathStr));
do {
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.lineTo(bx, by);
path.lineTo(cx, cy);
path.lineTo(dx, dy);
path.close();
path.moveTo(ex, ey);
path.lineTo(fx, fy);
path.lineTo(gx, gy);
path.lineTo(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.lineTo(%d, %d);\n", bx, by);
str += sprintf(str, " path.lineTo(%d, %d);\n", 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.lineTo(%d, %d);\n", gx, gy);
str += sprintf(str, " path.lineTo(%d, %d);\n", hx, hy);
str += sprintf(str, " path.close();\n");
}
outputProgress(state, pathStr, SkPath::kWinding_FillType);
testSimplifyx(path, false, out, state, pathStr);
state.testsRun++;
path.setFillType(SkPath::kEvenOdd_FillType);
outputProgress(state, pathStr, SkPath::kEvenOdd_FillType);
testSimplifyx(path, true, out, state, pathStr);
state.testsRun++;
}
}
}
}
} while (runNextTestSet(state));
return NULL;
}
void Simplify4x4QuadralateralsThreaded_Test(int& testsRun)
{
SkDebugf("%s\n", __FUNCTION__);
#ifdef SK_DEBUG
gDebugMaxWindSum = 4; // FIXME: 3?
gDebugMaxWindValue = 4;
#endif
const char testStr[] = "testQuadralateral";
initializeTests(testStr, sizeof(testStr));
int testsStart = testsRun;
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) {
testsRun += dispatchTest4(testSimplify4x4QuadralateralsMain,
a, b, c, d);
}
if (!gRunTestsInOneThread) SkDebugf(".");
}
if (!gRunTestsInOneThread) SkDebugf("%d", b);
}
if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
}
testsRun += waitForCompletion();
SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
}
static void* testSimplify4x4NondegeneratesMain(void* data) {
SkASSERT(data);
State4& state = *(State4*) data;
char pathStr[1024];
bzero(pathStr, sizeof(pathStr));
do {
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;
for (int d = 0; d < 15; ++d) {
int dx = d & 0x03;
int dy = d >> 2;
for (int e = d + 1; e < 16; ++e) {
int ex = e & 0x03;
int ey = e >> 2;
for (int f = d + 1; f < 16; ++f) {
if (e == f) {
continue;
}
int fx = f & 0x03;
int fy = f >> 2;
if ((ex - dx) * (fy - dy) == (ey - dy) * (fx - dx)) {
continue;
}
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(ax, ay);
path.lineTo(bx, by);
path.lineTo(cx, cy);
path.close();
path.moveTo(dx, dy);
path.lineTo(ex, ey);
path.lineTo(fx, fy);
path.close();
if (1) {
char* str = pathStr;
str += sprintf(str, " path.moveTo(%d, %d);\n", ax, ay);
str += sprintf(str, " path.lineTo(%d, %d);\n", bx, by);
str += sprintf(str, " path.lineTo(%d, %d);\n", cx, cy);
str += sprintf(str, " path.close();\n");
str += sprintf(str, " path.moveTo(%d, %d);\n", dx, dy);
str += sprintf(str, " path.lineTo(%d, %d);\n", ex, ey);
str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy);
str += sprintf(str, " path.close();\n");
}
outputProgress(state, pathStr, SkPath::kWinding_FillType);
testSimplifyx(path, false, out, state, pathStr);
state.testsRun++;
path.setFillType(SkPath::kEvenOdd_FillType);
outputProgress(state, pathStr, SkPath::kEvenOdd_FillType);
testSimplifyx(path, true, out, state, pathStr);
state.testsRun++;
}
}
}
} while (runNextTestSet(state));
return NULL;
}
void SimplifyNondegenerate4x4TrianglesThreaded_Test(int& testsRun) {
SkDebugf("%s\n", __FUNCTION__);
#ifdef SK_DEBUG
gDebugMaxWindSum = 2;
gDebugMaxWindValue = 2;
#endif
const char testStr[] = "testNondegenerate";
initializeTests(testStr, sizeof(testStr));
int testsStart = testsRun;
for (int a = 0; a < 15; ++a) {
int ax = a & 0x03;
int ay = a >> 2;
for (int b = a + 1; b < 16; ++b) {
int bx = b & 0x03;
int by = b >> 2;
for (int c = a + 1; c < 16; ++c) {
if (b == c) {
continue;
}
int cx = c & 0x03;
int cy = c >> 2;
if ((bx - ax) * (cy - ay) == (by - ay) * (cx - ax)) {
continue;
}
testsRun += dispatchTest4(testSimplify4x4NondegeneratesMain,
a, b, c, 0);
}
if (!gRunTestsInOneThread) SkDebugf(".");
}
if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
}
testsRun += waitForCompletion();
SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
}
static void* testSimplify4x4DegeneratesMain(void* data) {
SkASSERT(data);
State4& state = *(State4*) data;
char pathStr[1024];
bzero(pathStr, sizeof(pathStr));
do {
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;
for (int d = 0; d < 16; ++d) {
int dx = d & 0x03;
int dy = d >> 2;
for (int e = d ; e < 16; ++e) {
int ex = e & 0x03;
int ey = e >> 2;
for (int f = d ; f < 16; ++f) {
int fx = f & 0x03;
int fy = f >> 2;
if (state.d && (ex - dx) * (fy - dy)
!= (ey - dy) * (fx - dx)) {
continue;
}
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(ax, ay);
path.lineTo(bx, by);
path.lineTo(cx, cy);
path.close();
path.moveTo(dx, dy);
path.lineTo(ex, ey);
path.lineTo(fx, fy);
path.close();
if (1) {
char* str = pathStr;
str += sprintf(str, " path.moveTo(%d, %d);\n", ax, ay);
str += sprintf(str, " path.lineTo(%d, %d);\n", bx, by);
str += sprintf(str, " path.lineTo(%d, %d);\n", cx, cy);
str += sprintf(str, " path.close();\n");
str += sprintf(str, " path.moveTo(%d, %d);\n", dx, dy);
str += sprintf(str, " path.lineTo(%d, %d);\n", ex, ey);
str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy);
str += sprintf(str, " path.close();\n");
}
outputProgress(state, pathStr, SkPath::kWinding_FillType);
testSimplifyx(path, false, out, state, pathStr);
state.testsRun++;
path.setFillType(SkPath::kEvenOdd_FillType);
outputProgress(state, pathStr, SkPath::kEvenOdd_FillType);
testSimplifyx(path, true, out, state, pathStr);
state.testsRun++;
}
}
}
} while (runNextTestSet(state));
return NULL;
}
void SimplifyDegenerate4x4TrianglesThreaded_Test(int& testsRun) {
SkDebugf("%s\n", __FUNCTION__);
#ifdef SK_DEBUG
gDebugMaxWindSum = 2;
gDebugMaxWindValue = 2;
#endif
const char testStr[] = "testDegenerate";
initializeTests(testStr, sizeof(testStr));
int testsStart = testsRun;
for (int a = 0; a < 16; ++a) {
int ax = a & 0x03;
int ay = a >> 2;
for (int b = a ; b < 16; ++b) {
int bx = b & 0x03;
int by = b >> 2;
for (int c = a ; c < 16; ++c) {
int cx = c & 0x03;
int cy = c >> 2;
bool abcIsATriangle = (bx - ax) * (cy - ay) != (by - ay) * (cx - ax);
testsRun += dispatchTest4(testSimplify4x4DegeneratesMain,
a, b, c, abcIsATriangle);
}
if (!gRunTestsInOneThread) SkDebugf(".");
}
if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
}
testsRun += waitForCompletion();
SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,790 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "SkBitmap.h"
static SkBitmap bitmap;
static void testSimplifyTriangle() {
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(10,10); // triangle |\ .
path.lineTo(10,30); // |_\ .
path.lineTo(20,30);
path.close();
path.moveTo(20,10); // triangle /|
path.lineTo(10,30); // /_|
path.lineTo(20,30);
path.close();
testSimplify(path, true, out, bitmap); // expect |\/|
// |__|
}
static void testSimplifyTriangle3() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 1);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(3, 1);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle4() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 1);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(2, 1);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle5() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 1);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 1);
path.lineTo(2, 1);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle6() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(3, 1);
path.lineTo(0, 0);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle7() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 1);
path.lineTo(0, 2);
path.lineTo(0, 0);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle8() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 1);
path.lineTo(1, 2);
path.lineTo(1, 3);
path.lineTo(0, 1);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle9() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(1, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 1);
path.lineTo(2, 1);
path.lineTo(0, 0);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle10() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(1, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(2, 0);
path.lineTo(0, 1);
path.lineTo(0, 0);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle11() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 2);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(2, 1);
path.lineTo(2, 2);
path.lineTo(0, 0);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle12() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(1, 2);
path.lineTo(0, 0);
path.close();
path.moveTo(2, 0);
path.lineTo(0, 3);
path.lineTo(1, 1);
path.lineTo(2, 0);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle13() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 3);
path.lineTo(0, 0);
path.close();
path.moveTo(3, 0);
path.lineTo(0, 3);
path.lineTo(1, 1);
path.lineTo(3, 0);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle14() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 1);
path.lineTo(0, 0);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle15() {
SkPath path, out;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(1, 2);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(2, 2);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle16() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(1, 2);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(1, 3);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle17() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(1, 2);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 3);
path.lineTo(0, 1);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle18() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(1, 2);
path.close();
path.moveTo(1, 0);
path.lineTo(0, 1);
path.lineTo(0, 3);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle19() {
SkPath path, out;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(3, 2);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 1);
path.lineTo(2, 1);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle20() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(2, 1);
path.lineTo(1, 3);
path.close();
path.moveTo(2, 0);
path.lineTo(3, 2);
path.lineTo(0, 3);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle21() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(1, 2);
path.close();
path.moveTo(2, 0);
path.lineTo(2, 1);
path.lineTo(0, 3);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyDegenerateTriangle1() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 0);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.lineTo(0, 0);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyDegenerateTriangle2() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 1);
path.lineTo(2, 2);
path.close();
path.moveTo(1, 0);
path.lineTo(2, 2);
path.lineTo(3, 3);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyWindingParallelogram() {
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(20,10); // parallelogram _
path.lineTo(30,30); // \ \ .
path.lineTo(40,30); // \_\ .
path.lineTo(30,10);
path.close();
path.moveTo(20,10); // parallelogram _
path.lineTo(10,30); // / /
path.lineTo(20,30); // /_/
path.lineTo(30,10);
path.close();
testSimplify(path, true, out, bitmap); // expect _
// / \ .
} // /___\ .
static void testSimplifyXorParallelogram() {
SkPath path, out;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(20,10); // parallelogram _
path.lineTo(30,30); // \ \ .
path.lineTo(40,30); // \_\ .
path.lineTo(30,10);
path.close();
path.moveTo(20,10); // parallelogram _
path.lineTo(10,30); // / /
path.lineTo(20,30); // /_/
path.lineTo(30,10);
path.close();
testSimplify(path, true, out, bitmap); // expect _
} // \ /
static void testSimplifyTriangle2() {
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(10,10); // triangle |\ .
path.lineTo(10,30); // |_\ .
path.lineTo(20,30);
path.close();
path.moveTo(10,10); // triangle _
path.lineTo(20,10); // \ |
path.lineTo(20,30); // \|
path.close(); // _
testSimplify(path, true, out, bitmap); // expect | |
} // |_|
#if 0
static void testPathTriangleRendering() {
SkPath one, two;
one.moveTo(0, 0);
one.lineTo(3, 3);
one.lineTo(0, 3);
one.lineTo(1, 2);
one.close();
for (float x = .1f; x <= 2.9ff; x += .1f) {
SkDebugf("%s x=%g\n", __FUNCTION__, x);
two.moveTo(0, 0);
two.lineTo(x, x);
two.lineTo(3, 3);
two.lineTo(0, 3);
two.lineTo(1, 2);
two.close();
comparePaths(one, two);
two.reset();
}
}
#endif
static void simplify(const char* functionName, const SkPath& path,
bool fill, SkPath& out) {
if (false) SkDebugf("%s\n", functionName);
simplify(path, fill, out);
}
static void testSimplifySkinnyTriangle1() {
for (int x = 1; x < 255; ++x) {
SkPath path, out;
path.moveTo((x * 101) % 10, 0);
path.lineTo((x * 91) % 10, 1000);
path.lineTo((x * 71) % 10, 2000);
path.lineTo((x * 51) % 10, 3000);
path.close();
path.moveTo((x * 101) % 20, 0);
path.lineTo((x * 91) % 20, 1000);
path.lineTo((x * 71) % 20, 2000);
path.lineTo((x * 51) % 20, 3000);
path.close();
path.moveTo((x * 101) % 30, 0);
path.lineTo((x * 91) % 30, 1000);
path.lineTo((x * 71) % 30, 2000);
path.lineTo((x * 51) % 30, 3000);
path.close();
simplify(path, true, out);
}
}
static void testSimplifySkinnyTriangle2() {
SkPath path, out;
#if 01
path.moveTo(591.091064f, 627.534851f);
path.lineTo(541.088135f, 560.707642f);
path.lineTo(491.085175f, 493.880310f);
path.lineTo(441.082214f, 427.053101f);
//path.lineTo(591.091064f, 627.534851f);
path.close();
#endif
path.moveTo(317.093445f, 592.013306f);
path.lineTo(366.316162f, 542.986572f);
path.lineTo(416.051514f, 486.978577f);
path.lineTo(465.786865f, 430.970581f);
//path.lineTo(317.093445f, 592.013306f);
path.close();
#if 0
path.moveTo(289.392517f, 517.138489f);
path.lineTo(249.886078f, 508.598022f);
path.lineTo(217.110916f, 450.916443f);
path.lineTo(196.621033f, 394.917633f);
//path.lineTo(289.392517f, 517.138489f);
path.close();
#endif
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle3() {
SkPath path, out;
path.moveTo(591, 627.534851f);
path.lineTo(541, 560.707642f);
path.lineTo(491, 493.880310f);
path.lineTo(441, 427.053101f);
path.close();
path.moveTo(317, 592.013306f);
path.lineTo(366, 542.986572f);
path.lineTo(416, 486.978577f);
path.lineTo(465, 430.970581f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle4() {
SkPath path, out;
path.moveTo(572.655212f, 614.959961f);
path.lineTo(524.618896f, 549.339600f);
path.lineTo(476.582581f, 483.719269f);
path.lineTo(428.546265f, 418.098938f);
path.lineTo(572.655212f, 614.959961f);
path.close();
path.moveTo(312.166382f, 583.723083f);
path.lineTo(361.047791f, 529.824219f);
path.lineTo(409.929230f, 475.925354f);
path.lineTo(458.810669f, 422.026520f);
path.lineTo(312.166382f, 583.723083f);
path.close();
path.moveTo(278.742737f, 508.065643f);
path.lineTo(241.475800f, 493.465118f);
path.lineTo(210.344177f, 437.315125f);
path.lineTo(197.019455f, 383.794556f);
path.lineTo(278.742737f, 508.065643f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle5() {
SkPath path, out;
path.moveTo(554.690613f, 602.286072f);
path.lineTo(508.590057f, 537.906250f);
path.lineTo(462.489441f, 473.526520f);
path.lineTo(416.388855f, 409.146729f);
path.lineTo(554.690613f, 602.286072f);
path.close();
path.moveTo(307.216949f, 575.189270f);
path.lineTo(355.826965f, 516.804688f);
path.lineTo(403.815918f, 464.990753f);
path.lineTo(451.804871f, 413.176819f);
path.lineTo(307.216949f, 575.189270f);
path.close();
path.moveTo(271.998901f, 521.301025f);
path.lineTo(234.619705f, 499.687683f);
path.lineTo(203.059692f, 441.332336f);
path.lineTo(195.994370f, 386.856506f);
path.lineTo(271.998901f, 521.301025f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle6() {
SkPath path, out;
path.moveTo(591.091064f, 627.534851f);
path.lineTo(541.088135f, 560.707642f);
path.lineTo(491.085175f, 493.880310f);
path.lineTo(441.082214f, 427.053101f);
path.lineTo(591.091064f, 627.534851f);
path.close();
path.moveTo(317.093445f, 592.013306f);
path.lineTo(366.316162f, 542.986572f);
path.lineTo(416.051514f, 486.978577f);
path.lineTo(465.786865f, 430.970581f);
path.lineTo(317.093445f, 592.013306f);
path.close();
path.moveTo(289.392517f, 517.138489f);
path.lineTo(249.886078f, 508.598022f);
path.lineTo(217.110916f, 450.916443f);
path.lineTo(196.621033f, 394.917633f);
path.lineTo(289.392517f, 517.138489f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifyTriangle22() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 2);
path.close();
path.moveTo(1, 0);
path.lineTo(0, 2);
path.lineTo(0, 1);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle23() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 0);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 1);
path.lineTo(1, 2);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyTriangle24() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 0);
path.lineTo(0, 1);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(0, 1);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifySkinnyTriangle7() {
SkPath path, out;
path.moveTo(487.502319f, 550.811279f);
path.lineTo(448.826050f, 491.720123f);
path.lineTo(410.149780f, 432.628967f);
path.lineTo(371.473572f, 373.537781f);
path.lineTo(487.502319f, 550.811279f);
path.close();
path.moveTo(295.817108f, 532.655579f);
path.lineTo(342.896271f, 485.912292f);
path.lineTo(389.975433f, 439.169006f);
path.lineTo(437.054596f, 392.425781f);
path.lineTo(295.817108f, 532.655579f);
path.close();
path.moveTo(239.726822f, 575.025269f);
path.lineTo(204.117569f, 521.429688f);
path.lineTo(171.275452f, 454.110382f);
path.lineTo(193.328583f, 397.859497f);
path.lineTo(239.726822f, 575.025269f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle8() {
SkPath path, out;
path.moveTo(441.943115f, 511.678040f);
path.lineTo(408.487549f, 456.880920f);
path.lineTo(375.031952f, 402.083801f);
path.lineTo(341.576385f, 347.286682f);
path.lineTo(441.943115f, 511.678040f);
path.close();
path.moveTo(297.548492f, 557.246704f);
path.lineTo(350.768494f, 507.627014f);
path.lineTo(403.988525f, 458.007385f);
path.lineTo(457.208527f, 408.387695f);
path.lineTo(297.548492f, 557.246704f);
path.close();
path.moveTo(209.857895f, 615.802979f);
path.lineTo(178.249481f, 534.230347f);
path.lineTo(144.905640f, 460.056824f);
path.lineTo(192.953125f, 404.972900f);
path.lineTo(209.857895f, 615.802979f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle9() {
SkPath path, out;
path.moveTo(439.867065f, 528.291931f);
path.lineTo(405.413025f, 469.107178f);
path.lineTo(370.958954f, 409.922363f);
path.lineTo(336.504883f, 350.737610f);
path.lineTo(439.867065f, 528.291931f);
path.close();
path.moveTo(298.922455f, 573.251953f);
path.lineTo(356.360962f, 521.905090f);
path.lineTo(413.799438f, 470.558228f);
path.lineTo(471.237915f, 419.211365f);
path.lineTo(298.922455f, 573.251953f);
path.close();
path.moveTo(187.200775f, 643.035156f);
path.lineTo(159.713165f, 540.993774f);
path.lineTo(126.257164f, 462.198517f);
path.lineTo(193.534012f, 409.266235f);
path.lineTo(187.200775f, 643.035156f);
path.close();
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle10() {
SkPath path, out;
#if 0
path.moveTo(99.270325f, 239.365234f);
path.lineTo(105.967056f, 173.361206f);
path.lineTo(148.821381f, 141.309891f);
path.lineTo(159.101013f, 189.235138f);
path.lineTo(99.270325f, 239.365234f);
path.close();
#endif
path.moveTo(213.673737f, 413.292938f);
path.lineTo(225.200134f, 343.616821f);
path.lineTo(236.726532f, 273.940704f);
path.lineTo(219.386414f, 231.373322f);
path.lineTo(213.673737f, 413.292938f);
path.close();
path.moveTo(43.485352f, 308.984497f);
path.lineTo(122.610657f, 305.950134f);
path.lineTo(201.735962f, 302.915802f);
path.lineTo(280.861267f, 299.881470f);
path.lineTo(43.485352f, 308.984497f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle11() {
SkPath path, out;
path.moveTo(-177.878387f, 265.368988f);
path.lineTo(-254.415771f, 303.709961f);
path.lineTo(-317.465363f, 271.325562f);
path.lineTo(-374.520386f, 207.507660f);
path.lineTo(-177.878387f, 265.368988f);
path.close();
path.moveTo(-63.582489f, -3.679123f);
path.lineTo(-134.496841f, 26.434566f);
path.lineTo(-205.411209f, 56.548256f);
path.lineTo(-276.325562f, 86.661942f);
path.lineTo(-63.582489f, -3.679123f);
path.close();
path.moveTo(-57.078423f, 162.633453f);
path.lineTo(-95.963928f, 106.261139f);
path.lineTo(-134.849457f, 49.888824f);
path.lineTo(-173.734955f, -6.483480f);
path.lineTo(-57.078423f, 162.633453f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle12() {
SkPath path, out;
path.moveTo(98.666489f, -94.295059f);
path.lineTo(156.584320f, -61.939133f);
path.lineTo(174.672974f, -12.343765f);
path.lineTo(158.622345f, 52.028267f);
path.lineTo(98.666489f, -94.295059f);
path.close();
path.moveTo(-133.225616f, -48.622055f);
path.lineTo(-73.855499f, -10.375397f);
path.lineTo(-14.485367f, 27.871277f);
path.lineTo(44.884750f, 66.117935f);
path.lineTo(-133.225616f, -48.622055f);
path.close();
path.moveTo( 9.030045f, -163.413132f);
path.lineTo(-19.605331f, -89.588760f);
path.lineTo(-48.240707f, -15.764404f);
path.lineTo(-76.876053f, 58.059944f);
path.lineTo( 9.030045f, -163.413132f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle13() {
SkPath path, out;
path.moveTo(340.41568f, -170.97171f);
path.lineTo(418.846893f, -142.428329f);
path.lineTo(497.278107f, -113.884933f);
path.lineTo(449.18222f, -45.6723022f);
path.lineTo(340.41568f, -170.97171f);
path.close();
path.moveTo(326.610535f, 34.0393639f);
path.lineTo(371.334595f, -14.9620667f);
path.lineTo(416.058624f, -63.9634857f);
path.lineTo(460.782654f, -112.96492f);
path.lineTo(326.610535f, 34.0393639f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void (*simplifyTests[])() = {
testSimplifySkinnyTriangle13,
testSimplifySkinnyTriangle12,
testSimplifySkinnyTriangle11,
testSimplifySkinnyTriangle10,
testSimplifySkinnyTriangle9,
testSimplifySkinnyTriangle8,
testSimplifySkinnyTriangle7,
testSimplifySkinnyTriangle6,
testSimplifySkinnyTriangle5,
testSimplifySkinnyTriangle4,
testSimplifySkinnyTriangle3,
testSimplifySkinnyTriangle2,
testSimplifySkinnyTriangle1,
testSimplifyTriangle24,
testSimplifyTriangle23,
testSimplifyTriangle22,
testSimplifyDegenerateTriangle2,
testSimplifyDegenerateTriangle1,
testSimplifyTriangle21,
testSimplifyTriangle20,
testSimplifyTriangle19,
testSimplifyTriangle18,
testSimplifyTriangle17,
testSimplifyTriangle16,
testSimplifyTriangle15,
testSimplifyTriangle14,
testSimplifyTriangle13,
testSimplifyTriangle12,
testSimplifyTriangle11,
testSimplifyTriangle10,
testSimplifyTriangle7,
testSimplifyTriangle9,
testSimplifyTriangle8,
testSimplifyTriangle6,
testSimplifyTriangle5,
testSimplifyTriangle4,
testSimplifyTriangle3,
testSimplifyTriangle,
testSimplifyTriangle2,
testSimplifyWindingParallelogram,
testSimplifyXorParallelogram,
// testPathTriangleRendering,
};
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
static void (*firstTest)() = testSimplifySkinnyTriangle12;
void SimplifyPolygonPaths_Test() {
size_t index = 0;
if (firstTest) {
while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
++index;
}
}
bool firstTestComplete = false;
for ( ; index < simplifyTestsCount; ++index) {
(*simplifyTests[index])();
if (simplifyTests[index] == testSimplifySkinnyTriangle2) {
if (false) SkDebugf("%s last fast skinny test\n", __FUNCTION__);
}
firstTestComplete = true;
}
}

View File

@ -1,126 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "SkBitmap.h"
static SkBitmap bitmap;
static void testSimplifyQuad1() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(3, 2);
path.lineTo(3, 3);
path.close();
path.moveTo(1, 0);
path.lineTo(1, 3);
path.lineTo(1, 3);
path.lineTo(1, 3);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyQuad2() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 0);
path.lineTo(0, 0);
path.lineTo(0, 2);
path.close();
path.moveTo(0, 1);
path.lineTo(0, 1);
path.lineTo(1, 1);
path.lineTo(0, 2);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyQuad3() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 0);
path.lineTo(1, 0);
path.lineTo(1, 2);
path.close();
path.moveTo(0, 1);
path.lineTo(1, 1);
path.lineTo(2, 1);
path.lineTo(0, 2);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyQuad4() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 0);
path.lineTo(1, 0);
path.lineTo(2, 2);
path.close();
path.moveTo(0, 0);
path.lineTo(2, 1);
path.lineTo(3, 1);
path.lineTo(3, 3);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyQuad5() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 0);
path.lineTo(1, 0);
path.lineTo(3, 2);
path.close();
path.moveTo(0, 1);
path.lineTo(1, 1);
path.lineTo(2, 1);
path.lineTo(0, 2);
path.close();
testSimplify(path, true, out, bitmap);
}
static void testSimplifyQuad6() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(1, 0);
path.lineTo(1, 1);
path.lineTo(3, 3);
path.close();
path.moveTo(1, 1);
path.lineTo(1, 1);
path.lineTo(1, 1);
path.lineTo(2, 2);
path.close();
testSimplify(path, true, out, bitmap);
}
static void (*simplifyTests[])() = {
testSimplifyQuad6,
testSimplifyQuad5,
testSimplifyQuad4,
testSimplifyQuad3,
testSimplifyQuad2,
testSimplifyQuad1,
};
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
static void (*firstTest)() = 0;
void SimplifyQuadralateralPaths_Test() {
size_t index = 0;
if (firstTest) {
while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
++index;
}
}
for ( ; index < simplifyTestsCount; ++index) {
(*simplifyTests[index])();
}
}

View File

@ -1,106 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
static void* testSimplify4x4QuadraticsMain(void* data)
{
SkASSERT(data);
State4& state = *(State4*) data;
char pathStr[1024];
bzero(pathStr, sizeof(pathStr));
do {
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();\n");
}
outputProgress(state, pathStr, SkPath::kWinding_FillType);
testSimplifyx(path, false, out, state, pathStr);
state.testsRun++;
path.setFillType(SkPath::kEvenOdd_FillType);
outputProgress(state, pathStr, SkPath::kEvenOdd_FillType);
testSimplifyx(path, true, out, state, pathStr);
state.testsRun++;
}
}
}
}
} while (runNextTestSet(state));
return NULL;
}
void Simplify4x4QuadraticsThreaded_Test(int& testsRun)
{
SkDebugf("%s\n", __FUNCTION__);
#ifdef SK_DEBUG
gDebugMaxWindSum = 4; // FIXME: 3?
gDebugMaxWindValue = 4;
#endif
const char testStr[] = "testQuadratic";
initializeTests(testStr, sizeof(testStr));
int testsStart = testsRun;
int a = 0;
#define SKIP_A 0
#if SKIP_A
a = 2;
#endif
for (; a < 16; ++a) {
for (int b = a ; b < 16; ++b) {
for (int c = b ; c < 16; ++c) {
for (int d = c; d < 16; ++d) {
testsRun += dispatchTest4(testSimplify4x4QuadraticsMain,
a, b, c, d);
}
if (!gRunTestsInOneThread) SkDebugf(".");
}
if (!gRunTestsInOneThread) SkDebugf("%d", b);
}
if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
}
testsRun += waitForCompletion();
SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
}

View File

@ -1,284 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "SkBitmap.h"
static SkBitmap bitmap;
static void 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);
}
static void 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);
}
static void 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);
}
static void testSimplifyQuadratic4() {
SkPath path, out;
path.moveTo(0, 20);
path.quadTo(20, 0, 40, 20);
path.close();
path.moveTo(40, 10);
path.quadTo(20, 30, 0, 10);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void 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);
}
static void 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);
}
static void 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);
}
static void 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);
}
static void 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);
}
static void testSimplifyQuadratic10() {
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, 1);
path.quadTo(1, 1, 1, 2);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic11() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(0, 2);
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);
}
static void 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);
}
static void testSimplifyQuadratic13() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 1, 0);
path.lineTo(1, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.quadTo(3, 0, 1, 1);
path.lineTo(0, 0);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic14() {
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(0, 1, 2, 1);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void 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);
}
static void 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);
}
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,
testSimplifyQuadratic13,
testSimplifyQuadratic12,
testSimplifyQuadratic11,
testSimplifyQuadratic10,
testSimplifyQuadratic9,
testSimplifyQuadratic8,
testSimplifyQuadratic7,
testSimplifyQuadratic6,
testSimplifyQuadratic5,
testSimplifyQuadratic4,
testSimplifyQuadratic3,
testSimplifyQuadratic2,
testSimplifyQuadratic1,
};
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
static void (*firstTest)() = testSimplifyQuadratic14;
static bool skipAll = false;
void SimplifyQuadraticPaths_Test() {
if (skipAll) {
return;
}
size_t index = 0;
if (firstTest) {
while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
++index;
}
}
bool firstTestComplete = false;
for ( ; index < simplifyTestsCount; ++index) {
(*simplifyTests[index])();
if (simplifyTests[index] == testSimplifyQuadratic1) {
SkDebugf("%s last fast quad test\n", __FUNCTION__);
}
firstTestComplete = true;
}
}

View File

@ -1,469 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "SkBitmap.h"
static SkBitmap bitmap;
static void testSimplifyCoincidentInner() {
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.addRect(10, 10, 60, 60, SkPath::kCCW_Direction);
path.addRect(20, 20, 50, 50, SkPath::kCW_Direction);
path.addRect(20, 30, 40, 40, SkPath::kCW_Direction);
testSimplify(path, true, out, bitmap);
}
static void testSimplifyCoincidentVertical() {
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.addRect(10, 10, 30, 30);
path.addRect(10, 30, 30, 40);
simplify(path, true, out);
SkRect rect;
if (!out.isRect(&rect)) {
SkDebugf("%s expected rect\n", __FUNCTION__);
}
if (rect != SkRect::MakeLTRB(10, 10, 30, 40)) {
SkDebugf("%s expected union\n", __FUNCTION__);
}
}
static void testSimplifyCoincidentHorizontal() {
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.addRect(10, 10, 30, 30);
path.addRect(30, 10, 40, 30);
simplify(path, true, out);
SkRect rect;
if (!out.isRect(&rect)) {
SkDebugf("%s expected rect\n", __FUNCTION__);
}
if (rect != SkRect::MakeLTRB(10, 10, 40, 30)) {
SkDebugf("%s expected union\n", __FUNCTION__);
}
}
static void testSimplifyMulti() {
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.addRect(10, 10, 30, 30);
path.addRect(20, 20, 40, 40);
simplify(path, true, out);
SkPath expected;
expected.setFillType(SkPath::kEvenOdd_FillType);
expected.moveTo(10,10); // two cutout corners
expected.lineTo(10,30);
expected.lineTo(20,30);
expected.lineTo(20,40);
expected.lineTo(40,40);
expected.lineTo(40,20);
expected.lineTo(30,20);
expected.lineTo(30,10);
expected.lineTo(10,10);
expected.close();
if (out != expected) {
SkDebugf("%s expected equal\n", __FUNCTION__);
}
path = out;
path.addRect(30, 10, 40, 20);
path.addRect(10, 30, 20, 40);
simplify(path, true, out);
SkRect rect;
if (!out.isRect(&rect)) {
SkDebugf("%s expected rect\n", __FUNCTION__);
}
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
SkDebugf("%s expected union\n", __FUNCTION__);
}
path = out;
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
simplify(path, true, out);
if (!out.isEmpty()) {
SkDebugf("%s expected empty\n", __FUNCTION__);
}
}
static void testSimplifyAddL() {
SkPath path, out;
path.moveTo(10,10); // 'L' shape
path.lineTo(10,40);
path.lineTo(40,40);
path.lineTo(40,20);
path.lineTo(30,20);
path.lineTo(30,10);
path.lineTo(10,10);
path.close();
path.addRect(30, 10, 40, 20); // missing notch of 'L'
simplify(path, true, out);
SkRect rect;
if (!out.isRect(&rect)) {
SkDebugf("%s expected rect\n", __FUNCTION__);
}
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
SkDebugf("%s expected union\n", __FUNCTION__);
}
}
static void testSimplifyCoincidentCCW() {
SkPath path, out;
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
simplify(path, true, out);
SkRect rect;
if (!out.isRect(&rect)) {
SkDebugf("%s expected rect\n", __FUNCTION__);
}
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
SkDebugf("%s expected union\n", __FUNCTION__);
}
}
static void testSimplifyCoincidentCW() {
SkPath path, out;
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
path.addRect(10, 10, 40, 40, SkPath::kCW_Direction);
simplify(path, true, out);
if (!out.isEmpty()) {
SkDebugf("%s expected empty\n", __FUNCTION__);
}
}
static void testSimplifyCorner() {
SkPath path, out;
path.addRect(10, 10, 20, 20, SkPath::kCCW_Direction);
path.addRect(20, 20, 40, 40, SkPath::kCW_Direction);
simplify(path, true, out);
SkTDArray<SkRect> boundsArray;
contourBounds(out, boundsArray);
if (boundsArray.count() != 2) {
SkDebugf("%s expected 2 contours\n", __FUNCTION__);
return;
}
SkRect one = SkRect::MakeLTRB(10, 10, 20, 20);
SkRect two = SkRect::MakeLTRB(20, 20, 40, 40);
if ((boundsArray[0] != one && boundsArray[0] != two)
|| (boundsArray[1] != one && boundsArray[1] != two)) {
SkDebugf("%s expected match\n", __FUNCTION__);
}
}
static void testSimplifyDiagonal() {
SkRect rect2 = SkRect::MakeXYWH(10, 10, 10, 10);
for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
for (int x = 0; x <= 20; x += 20) {
for (int y = 0; y <= 20; y += 20) {
SkPath path, out;
SkRect rect1 = SkRect::MakeXYWH(x, y, 10, 10);
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
simplify(path, true, out);
SkPath::Iter iter(out, false);
SkPoint pts[4], lastLine[2];
SkPath::Verb verb;
SkRect bounds[2];
bounds[0].setEmpty();
bounds[1].setEmpty();
SkRect* boundsPtr = bounds;
int count = 0, segments = 0;
bool lastLineSet = false;
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
if (!boundsPtr->isEmpty()) {
SkASSERT(boundsPtr == bounds);
++boundsPtr;
}
boundsPtr->set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
count = 0;
lastLineSet = false;
break;
case SkPath::kLine_Verb:
if (lastLineSet) {
SkASSERT((lastLine[1].fX - lastLine[0].fX) *
(pts[1].fY - lastLine[0].fY) !=
(lastLine[1].fY - lastLine[0].fY) *
(pts[1].fX - lastLine[0].fX));
}
lastLineSet = true;
lastLine[0] = pts[0];
lastLine[1] = pts[1];
count = 1;
++segments;
break;
case SkPath::kClose_Verb:
count = 0;
break;
default:
SkDEBUGFAIL("bad verb");
return;
}
for (int i = 1; i <= count; ++i) {
boundsPtr->growToInclude(pts[i].fX, pts[i].fY);
}
}
if (boundsPtr != bounds) {
SkASSERT((bounds[0] == rect1 || bounds[1] == rect1)
&& (bounds[0] == rect2 || bounds[1] == rect2));
} else {
SkASSERT(segments == 8);
}
}
}
}
}
}
static void assertOneContour(const SkPath& out, bool edge, bool extend) {
SkPath::Iter iter(out, false);
SkPoint pts[4];
SkPath::Verb verb;
SkRect bounds;
bounds.setEmpty();
int count = 0;
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
SkASSERT(count == 0);
break;
case SkPath::kLine_Verb:
SkASSERT(pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY);
++count;
break;
case SkPath::kClose_Verb:
break;
default:
SkDEBUGFAIL("bad verb");
return;
}
}
SkASSERT(count == (extend ? 4 : edge ? 6 : 8));
}
static void testSimplifyCoincident() {
// outside to inside, outside to right, outside to outside
// left to inside, left to right, left to outside
// inside to right, inside to outside
// repeat above for left, right, bottom
SkScalar start[] = { 0, 10, 20 };
size_t startCount = sizeof(start) / sizeof(start[0]);
SkScalar stop[] = { 30, 40, 50 };
size_t stopCount = sizeof(stop) / sizeof(stop[0]);
SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
bool extend = start[startIndex] == rect2.fLeft && stop[stopIndex] == rect2.fRight;
bool edge = start[startIndex] == rect2.fLeft || stop[stopIndex] == rect2.fRight;
SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 10);
SkPath path, out;
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
simplify(path, true, out);
assertOneContour(out, edge, extend);
path.reset();
rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 50);
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
simplify(path, true, out);
assertOneContour(out, edge, extend);
path.reset();
rect1 = SkRect::MakeLTRB(0, start[startIndex], 10, stop[stopIndex]);
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
simplify(path, true, out);
assertOneContour(out, edge, extend);
path.reset();
rect1 = SkRect::MakeLTRB(40, start[startIndex], 50, stop[stopIndex]);
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
simplify(path, true, out);
assertOneContour(out, edge, extend);
}
}
}
}
}
static void testSimplifyOverlap() {
SkScalar start[] = { 0, 10, 20 };
size_t startCount = sizeof(start) / sizeof(start[0]);
SkScalar stop[] = { 30, 40, 50 };
size_t stopCount = sizeof(stop) / sizeof(stop[0]);
SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
for (size_t lefty = 0; lefty < startCount; ++lefty) {
for (size_t righty = 0; righty < stopCount; ++righty) {
for (size_t toppy = 0; toppy < startCount; ++toppy) {
for (size_t botty = 0; botty < stopCount; ++botty) {
SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
stop[righty], stop[botty]);
SkPath path, out;
path.addRect(rect1, static_cast<SkPath::Direction>(dir));
path.addRect(rect2, static_cast<SkPath::Direction>(dir));
testSimplify(path, true, out, bitmap);
}
}
}
}
}
}
static void testSimplifyOverlapTiny() {
SkScalar start[] = { 0, 1, 2 };
size_t startCount = sizeof(start) / sizeof(start[0]);
SkScalar stop[] = { 3, 4, 5 };
size_t stopCount = sizeof(stop) / sizeof(stop[0]);
SkRect rect2 = SkRect::MakeXYWH(1, 1, 3, 3);
for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
for (size_t lefty = 0; lefty < startCount; ++lefty) {
for (size_t righty = 0; righty < stopCount; ++righty) {
for (size_t toppy = 0; toppy < startCount; ++toppy) {
for (size_t botty = 0; botty < stopCount; ++botty) {
SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
stop[righty], stop[botty]);
SkPath path, out;
path.addRect(rect1, static_cast<SkPath::Direction>(dir));
path.addRect(rect2, static_cast<SkPath::Direction>(dir));
simplify(path, true, out);
comparePathsTiny(path, out);
}
}
}
}
}
}
static void testSimplifyDegenerate() {
SkScalar start[] = { 0, 10, 20 };
size_t startCount = sizeof(start) / sizeof(start[0]);
SkScalar stop[] = { 30, 40, 50 };
size_t stopCount = sizeof(stop) / sizeof(stop[0]);
SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 0);
SkPath path, out;
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
simplify(path, true, out);
SkRect rect;
if (!out.isRect(&rect)) {
SkDebugf("%s 1 expected rect\n", __FUNCTION__);
}
if (rect != rect2) {
SkDebugf("%s 1 expected union\n", __FUNCTION__);
}
path.reset();
rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 40);
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
simplify(path, true, out);
if (!out.isRect(&rect)) {
SkDebugf("%s 2 expected rect\n", __FUNCTION__);
}
if (rect != rect2) {
SkDebugf("%s 2 expected union\n", __FUNCTION__);
}
path.reset();
rect1 = SkRect::MakeLTRB(0, start[startIndex], 0, stop[stopIndex]);
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
simplify(path, true, out);
if (!out.isRect(&rect)) {
SkDebugf("%s 3 expected rect\n", __FUNCTION__);
}
if (rect != rect2) {
SkDebugf("%s 3 expected union\n", __FUNCTION__);
}
path.reset();
rect1 = SkRect::MakeLTRB(40, start[startIndex], 40, stop[stopIndex]);
path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
simplify(path, true, out);
if (!out.isRect(&rect)) {
SkDebugf("%s 4 expected rect\n", __FUNCTION__);
}
if (rect != rect2) {
SkDebugf("%s 4 expected union\n", __FUNCTION__);
}
}
}
}
}
}
static void testSimplifyDegenerate1() {
SkPath path, out;
path.setFillType(SkPath::kWinding_FillType);
path.addRect( 0, 0, 0, 30);
path.addRect(10, 10, 40, 40);
simplify(path, true, out);
SkRect rect;
if (!out.isRect(&rect)) {
SkDebugf("%s expected rect\n", __FUNCTION__);
}
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
SkDebugf("%s expected union\n", __FUNCTION__);
}
}
static void (*simplifyTests[])() = {
testSimplifyCoincidentInner,
testSimplifyOverlapTiny,
testSimplifyDegenerate1,
testSimplifyCorner,
testSimplifyDegenerate,
testSimplifyOverlap,
testSimplifyDiagonal,
testSimplifyCoincident,
testSimplifyCoincidentCW,
testSimplifyCoincidentCCW,
testSimplifyCoincidentVertical,
testSimplifyCoincidentHorizontal,
testSimplifyAddL,
testSimplifyMulti,
};
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
static void (*firstTest)() = 0;
void SimplifyRectangularPaths_Test() {
size_t index = 0;
if (firstTest) {
while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
++index;
}
}
for ( ; index < simplifyTestsCount; ++index) {
if (simplifyTests[index] == testSimplifyCorner) {
// testSimplifyCorner fails because it expects two contours, where
// only one is returned. Both results are reasonable, but if two
// contours are desirable, or if we provide an option to choose
// between longer contours and more contours, turn this back on. For
// the moment, testSimplifyDiagonal also checks the test case, and
// permits either two rects or one non-crossing poly as valid
// unreported results.
continue;
}
(*simplifyTests[index])();
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "ShapeOps.h"
#include "SkBitmap.h"
#include "SkStream.h"
#include <pthread.h>
struct State4;
//extern int comparePaths(const SkPath& one, const SkPath& two);
extern int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap);
extern void comparePathsTiny(const SkPath& one, const SkPath& two);
extern bool drawAsciiPaths(const SkPath& one, const SkPath& two,
bool drawPaths);
extern void showOp(const ShapeOp op);
extern void showPath(const SkPath& path, const char* str);
extern void showPath(const SkPath& path);
extern void showPathData(const SkPath& path);
extern bool testSimplify(const SkPath& path, bool fill, SkPath& out,
SkBitmap& bitmap);
extern bool testSimplifyx(SkPath& path, bool useXor, SkPath& out,
State4& state, const char* pathStr);
extern bool testSimplifyx(const SkPath& path);
extern bool testShapeOp(const SkPath& a, const SkPath& b, const ShapeOp );
struct State4 {
State4();
static pthread_mutex_t addQueue;
static pthread_cond_t checkQueue;
pthread_cond_t initialized;
static State4* queue;
pthread_t threadID;
int index;
bool done;
bool last;
int a;
int b;
int c;
int d; // sometimes 1 if abc_is_a_triangle
int testsRun;
char filename[256];
SkBitmap bitmap;
};
void createThread(State4* statePtr, void* (*test)(void* ));
int dispatchTest4(void* (*testFun)(void* ), int a, int b, int c, int d);
void initializeTests(const char* testName, size_t testNameSize);
void outputProgress(const State4& state, const char* pathStr, SkPath::FillType );
void outputProgress(const State4& state, const char* pathStr, ShapeOp op);
void outputToStream(const State4& state, const char* pathStr, const char* pathPrefix,
const char* nameSuffix,
const char* testFunction, SkWStream& outFile);
bool runNextTestSet(State4& state);
int waitForCompletion();

View File

@ -1,761 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkMatrix.h"
#include "SkPaint.h"
#include "SkStream.h"
#include <algorithm>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#undef SkASSERT
#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
static const char marker[] =
"</div>\n"
"\n"
"<script type=\"text/javascript\">\n"
"\n"
"var testDivs = [\n";
static const char* opStrs[] = {
"kDifference_Op",
"kIntersect_Op",
"kUnion_Op",
"kXor_Op",
};
static const char* opSuffixes[] = {
"d",
"i",
"u",
"x",
};
static const char preferredFilename[] = "/flash/debug/XX.txt";
static const char backupFilename[] = "../../experimental/Intersection/debugXX.txt";
static bool gShowPath = false;
static bool gComparePaths = true;
static bool gShowOutputProgress = false;
static bool gComparePathsAssert = true;
static bool gPathStrAssert = true;
static bool gUsePhysicalFiles = false;
static void showPathContour(SkPath::Iter& iter) {
uint8_t verb;
SkPoint pts[4];
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
SkDebugf("path.moveTo(%1.9g,%1.9g);\n", pts[0].fX, pts[0].fY);
continue;
case SkPath::kLine_Verb:
SkDebugf("path.lineTo(%1.9g,%1.9g);\n", pts[1].fX, pts[1].fY);
break;
case SkPath::kQuad_Verb:
SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
break;
case SkPath::kCubic_Verb:
SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
break;
case SkPath::kClose_Verb:
SkDebugf("path.close();\n");
break;
default:
SkDEBUGFAIL("bad verb");
return;
}
}
}
void showPath(const SkPath& path, const char* str) {
SkDebugf("%s\n", !str ? "original:" : str);
showPath(path);
}
void showPath(const SkPath& path) {
SkPath::Iter iter(path, true);
int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
if (rectCount > 0) {
SkTDArray<SkRect> rects;
SkTDArray<SkPath::Direction> directions;
rects.setCount(rectCount);
directions.setCount(rectCount);
path.rectContours(rects.begin(), directions.begin());
for (int contour = 0; contour < rectCount; ++contour) {
const SkRect& rect = rects[contour];
SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
}
return;
}
iter.setPath(path, true);
showPathContour(iter);
}
void showPathData(const SkPath& path) {
SkPath::Iter iter(path, true);
uint8_t verb;
SkPoint pts[4];
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
continue;
case SkPath::kLine_Verb:
SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
break;
case SkPath::kQuad_Verb:
SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
break;
case SkPath::kCubic_Verb:
SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
break;
case SkPath::kClose_Verb:
break;
default:
SkDEBUGFAIL("bad verb");
return;
}
}
}
void showOp(const ShapeOp op) {
switch (op) {
case kDifference_Op:
SkDebugf("op difference\n");
break;
case kIntersect_Op:
SkDebugf("op intersect\n");
break;
case kUnion_Op:
SkDebugf("op union\n");
break;
case kXor_Op:
SkDebugf("op xor\n");
break;
default:
SkASSERT(0);
}
}
static void showPath(const SkPath& path, const char* str, const SkMatrix& scale) {
SkPath scaled;
SkMatrix inverse;
bool success = scale.invert(&inverse);
if (!success) SkASSERT(0);
path.transform(inverse, &scaled);
showPath(scaled, str);
}
const int bitWidth = 64;
const int bitHeight = 64;
static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
SkRect larger = one.getBounds();
larger.join(two.getBounds());
SkScalar largerWidth = larger.width();
if (largerWidth < 4) {
largerWidth = 4;
}
SkScalar largerHeight = larger.height();
if (largerHeight < 4) {
largerHeight = 4;
}
SkScalar hScale = (bitWidth - 2) / largerWidth;
SkScalar vScale = (bitHeight - 2) / largerHeight;
scale.reset();
scale.preScale(hScale, vScale);
}
static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
int& error2x2) {
if (bits.width() == 0) {
bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
bits.allocPixels();
}
SkCanvas canvas(bits);
canvas.drawColor(SK_ColorWHITE);
SkPaint paint;
canvas.save();
const SkRect& bounds1 = scaledOne.getBounds();
canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
canvas.drawPath(scaledOne, paint);
canvas.restore();
canvas.save();
canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
canvas.drawPath(scaledTwo, paint);
canvas.restore();
int errors2 = 0;
int errors = 0;
for (int y = 0; y < bitHeight - 1; ++y) {
uint32_t* addr1 = bits.getAddr32(0, y);
uint32_t* addr2 = bits.getAddr32(0, y + 1);
uint32_t* addr3 = bits.getAddr32(bitWidth, y);
uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
for (int x = 0; x < bitWidth - 1; ++x) {
// count 2x2 blocks
bool err = addr1[x] != addr3[x];
if (err) {
errors2 += addr1[x + 1] != addr3[x + 1]
&& addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
errors++;
}
}
}
if (errors2 >= 6 || errors > 160) {
SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors);
}
error2x2 = errors2;
return errors;
}
static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
SkPath& scaledTwo, int& error2x2) {
SkMatrix scale;
scaleMatrix(one, two, scale);
one.transform(scale, &scaledOne);
two.transform(scale, &scaledTwo);
return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
}
bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
if (!drawPaths) {
return true;
}
const SkRect& bounds1 = one.getBounds();
const SkRect& bounds2 = two.getBounds();
SkRect larger = bounds1;
larger.join(bounds2);
SkBitmap bits;
char out[256];
int bitWidth = SkScalarCeil(larger.width()) + 2;
if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
return false;
}
int bitHeight = SkScalarCeil(larger.height()) + 2;
if (bitHeight >= (int) sizeof(out)) {
return false;
}
bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
bits.allocPixels();
SkCanvas canvas(bits);
canvas.drawColor(SK_ColorWHITE);
SkPaint paint;
canvas.save();
canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
canvas.drawPath(one, paint);
canvas.restore();
canvas.save();
canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
canvas.drawPath(two, paint);
canvas.restore();
for (int y = 0; y < bitHeight; ++y) {
uint32_t* addr1 = bits.getAddr32(0, y);
int x;
char* outPtr = out;
for (x = 0; x < bitWidth; ++x) {
*outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
}
*outPtr++ = '|';
for (x = bitWidth; x < bitWidth * 2; ++x) {
*outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
}
*outPtr++ = '\0';
SkDebugf("%s\n", out);
}
return true;
}
static void showSimplifiedPath(const SkPath& one, const SkPath& two,
const SkPath& scaledOne, const SkPath& scaledTwo) {
showPath(one, "original:");
showPath(two, "simplified:");
drawAsciiPaths(scaledOne, scaledTwo, true);
}
int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap) {
int errors2x2;
SkPath scaledOne, scaledTwo;
int errors = pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
if (errors2x2 == 0) {
return 0;
}
const int MAX_ERRORS = 9;
if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
showSimplifiedPath(one, two, scaledOne, scaledTwo);
}
if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
showSimplifiedPath(one, two, scaledOne, scaledTwo);
SkASSERT(0);
}
return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
}
static void showShapeOpPath(const SkPath& one, const SkPath& two, const SkPath& a, const SkPath& b,
const SkPath& scaledOne, const SkPath& scaledTwo, const ShapeOp shapeOp,
const SkMatrix& scale) {
SkASSERT((unsigned) shapeOp < sizeof(opStrs) / sizeof(opStrs[0]));
showPath(a, "minuend:");
SkDebugf("op: %s\n", opStrs[shapeOp]);
showPath(b, "subtrahend:");
// the region often isn't very helpful since it approximates curves with a lot of line-tos
if (0) showPath(scaledOne, "region:", scale);
showPath(two, "op result:");
drawAsciiPaths(scaledOne, scaledTwo, true);
}
static int comparePaths(const SkPath& one, const SkPath& scaledOne, const SkPath& two,
const SkPath& scaledTwo,
SkBitmap& bitmap, const SkPath& a, const SkPath& b, const ShapeOp shapeOp,
const SkMatrix& scale) {
int errors2x2;
int errors = pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
if (errors2x2 == 0) {
return 0;
}
const int MAX_ERRORS = 8;
if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
showShapeOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
}
if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
showShapeOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
SkASSERT(0);
}
return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
}
// doesn't work yet
void comparePathsTiny(const SkPath& one, const SkPath& two) {
const SkRect& bounds1 = one.getBounds();
const SkRect& bounds2 = two.getBounds();
SkRect larger = bounds1;
larger.join(bounds2);
SkBitmap bits;
int bitWidth = SkScalarCeil(larger.width()) + 2;
int bitHeight = SkScalarCeil(larger.height()) + 2;
bits.setConfig(SkBitmap::kA1_Config, bitWidth * 2, bitHeight);
bits.allocPixels();
SkCanvas canvas(bits);
canvas.drawColor(SK_ColorWHITE);
SkPaint paint;
canvas.save();
canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
canvas.drawPath(one, paint);
canvas.restore();
canvas.save();
canvas.translate(-bounds2.fLeft + 1, -bounds2.fTop + 1);
canvas.drawPath(two, paint);
canvas.restore();
for (int y = 0; y < bitHeight; ++y) {
uint8_t* addr1 = bits.getAddr1(0, y);
uint8_t* addr2 = bits.getAddr1(bitWidth, y);
for (unsigned x = 0; x < bits.rowBytes(); ++x) {
SkASSERT(addr1[x] == addr2[x]);
}
}
}
bool testSimplify(const SkPath& path, bool fill, SkPath& out, SkBitmap& bitmap) {
if (gShowPath) {
showPath(path);
}
simplify(path, fill, out);
if (!gComparePaths) {
return true;
}
return comparePaths(path, out, bitmap) == 0;
}
bool testSimplifyx(SkPath& path, bool useXor, SkPath& out, State4& state,
const char* pathStr) {
SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
path.setFillType(fillType);
if (gShowPath) {
showPath(path);
}
simplifyx(path, out);
if (!gComparePaths) {
return true;
}
int result = comparePaths(path, out, state.bitmap);
if (result && gPathStrAssert) {
SkDebugf("addTest %s\n", state.filename);
char temp[8192];
bzero(temp, sizeof(temp));
SkMemoryWStream stream(temp, sizeof(temp));
const char* pathPrefix = NULL;
const char* nameSuffix = NULL;
if (fillType == SkPath::kEvenOdd_FillType) {
pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
nameSuffix = "x";
}
const char testFunction[] = "testSimplifyx(path);";
outputToStream(state, pathStr, pathPrefix, nameSuffix, testFunction, stream);
SkDebugf(temp);
SkASSERT(0);
}
return result == 0;
}
bool testSimplifyx(const SkPath& path) {
SkPath out;
simplifyx(path, out);
SkBitmap bitmap;
int result = comparePaths(path, out, bitmap);
if (result && gPathStrAssert) {
SkASSERT(0);
}
return result == 0;
}
bool testShapeOp(const SkPath& a, const SkPath& b, const ShapeOp shapeOp) {
#if FORCE_RELEASE == 0
showPathData(a);
showOp(shapeOp);
showPathData(b);
#endif
SkPath out;
operate(a, b, shapeOp, out);
SkPath pathOut, scaledPathOut;
SkRegion rgnA, rgnB, openClip, rgnOut;
openClip.setRect(-16000, -16000, 16000, 16000);
rgnA.setPath(a, openClip);
rgnB.setPath(b, openClip);
rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
rgnOut.getBoundaryPath(&pathOut);
SkMatrix scale;
scaleMatrix(a, b, scale);
SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
SkPath scaledA, scaledB;
scaledA.addPath(a, scale);
scaledA.setFillType(a.getFillType());
scaledB.addPath(b, scale);
scaledB.setFillType(b.getFillType());
scaledRgnA.setPath(scaledA, openClip);
scaledRgnB.setPath(scaledB, openClip);
scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
scaledRgnOut.getBoundaryPath(&scaledPathOut);
SkBitmap bitmap;
SkPath scaledOut;
scaledOut.addPath(out, scale);
scaledOut.setFillType(out.getFillType());
int result = comparePaths(pathOut, scaledPathOut, out, scaledOut, bitmap, a, b, shapeOp, scale);
if (result && gPathStrAssert) {
SkASSERT(0);
}
return result == 0;
}
const int maxThreadsAllocated = 64;
static int maxThreads = 1;
static int threadIndex;
State4 threadState[maxThreadsAllocated];
static int testNumber;
static const char* testName;
static bool debugThreads = false;
State4* State4::queue = NULL;
pthread_mutex_t State4::addQueue = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t State4::checkQueue = PTHREAD_COND_INITIALIZER;
State4::State4() {
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100);
bitmap.allocPixels();
}
void createThread(State4* statePtr, void* (*testFun)(void* )) {
int threadError = pthread_create(&statePtr->threadID, NULL, testFun,
(void*) statePtr);
SkASSERT(!threadError);
}
int dispatchTest4(void* (*testFun)(void* ), int a, int b, int c, int d) {
int testsRun = 0;
State4* statePtr;
if (!gRunTestsInOneThread) {
pthread_mutex_lock(&State4::addQueue);
if (threadIndex < maxThreads) {
statePtr = &threadState[threadIndex];
statePtr->testsRun = 0;
statePtr->a = a;
statePtr->b = b;
statePtr->c = c;
statePtr->d = d;
statePtr->done = false;
statePtr->index = threadIndex;
statePtr->last = false;
if (debugThreads) SkDebugf("%s %d create done=%d last=%d\n", __FUNCTION__,
statePtr->index, statePtr->done, statePtr->last);
pthread_cond_init(&statePtr->initialized, NULL);
++threadIndex;
createThread(statePtr, testFun);
} else {
while (!State4::queue) {
if (debugThreads) SkDebugf("%s checkQueue\n", __FUNCTION__);
pthread_cond_wait(&State4::checkQueue, &State4::addQueue);
}
statePtr = State4::queue;
testsRun += statePtr->testsRun;
statePtr->testsRun = 0;
statePtr->a = a;
statePtr->b = b;
statePtr->c = c;
statePtr->d = d;
statePtr->done = false;
State4::queue = NULL;
for (int index = 0; index < maxThreads; ++index) {
if (threadState[index].done) {
State4::queue = &threadState[index];
}
}
if (debugThreads) SkDebugf("%s %d init done=%d last=%d queued=%d\n", __FUNCTION__,
statePtr->index, statePtr->done, statePtr->last,
State4::queue ? State4::queue->index : -1);
pthread_cond_signal(&statePtr->initialized);
}
pthread_mutex_unlock(&State4::addQueue);
} else {
statePtr = &threadState[0];
testsRun += statePtr->testsRun;
statePtr->testsRun = 0;
statePtr->a = a;
statePtr->b = b;
statePtr->c = c;
statePtr->d = d;
statePtr->done = false;
statePtr->index = threadIndex;
statePtr->last = false;
(*testFun)(statePtr);
}
return testsRun;
}
void initializeTests(const char* test, size_t testNameSize) {
testName = test;
if (!gRunTestsInOneThread) {
int threads = -1;
size_t size = sizeof(threads);
sysctlbyname("hw.logicalcpu_max", &threads, &size, NULL, 0);
if (threads > 0) {
maxThreads = threads;
} else {
maxThreads = 8;
}
}
SkFILEStream inFile("../../experimental/Intersection/op.htm");
if (inFile.isValid()) {
SkTDArray<char> inData;
inData.setCount(inFile.getLength());
size_t inLen = inData.count();
inFile.read(inData.begin(), inLen);
inFile.setPath(NULL);
char* insert = strstr(inData.begin(), marker);
if (insert) {
insert += sizeof(marker) - 1;
const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
testNumber = atoi(numLoc) + 1;
}
}
const char* filename = preferredFilename;
SkFILEWStream preferredTest(filename);
if (!preferredTest.isValid()) {
filename = backupFilename;
SkFILEWStream backupTest(filename);
SkASSERT(backupTest.isValid());
}
for (int index = 0; index < maxThreads; ++index) {
State4* statePtr = &threadState[index];
strcpy(statePtr->filename, filename);
size_t len = strlen(filename);
SkASSERT(statePtr->filename[len - 6] == 'X');
SkASSERT(statePtr->filename[len - 5] == 'X');
statePtr->filename[len - 6] = '0' + index / 10;
statePtr->filename[len - 5] = '0' + index % 10;
}
threadIndex = 0;
}
void outputProgress(const State4& state, const char* pathStr, SkPath::FillType pathFillType) {
if (gRunTestsInOneThread && gShowOutputProgress) {
if (pathFillType == SkPath::kEvenOdd_FillType) {
SkDebugf(" path.setFillType(SkPath::kEvenOdd_FillType);\n", pathStr);
}
SkDebugf("%s\n", pathStr);
}
const char testFunction[] = "testSimplifyx(path);";
const char* pathPrefix = NULL;
const char* nameSuffix = NULL;
if (pathFillType == SkPath::kEvenOdd_FillType) {
pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
nameSuffix = "x";
}
if (gUsePhysicalFiles) {
SkFILEWStream outFile(state.filename);
if (!outFile.isValid()) {
SkASSERT(0);
return;
}
outputToStream(state, pathStr, pathPrefix, nameSuffix, testFunction, outFile);
return;
}
SkFILEWStream outRam(state.filename);
outputToStream(state, pathStr, pathPrefix, nameSuffix, testFunction, outRam);
}
void outputProgress(const State4& state, const char* pathStr, ShapeOp op) {
SkString testFunc("testShapeOp(path, pathB, ");
testFunc += opStrs[op];
testFunc += ");";
const char* testFunction = testFunc.c_str();
if (gRunTestsInOneThread && gShowOutputProgress) {
SkDebugf("%s\n", pathStr);
SkDebugf(" %s\n", testFunction);
}
const char* nameSuffix = opSuffixes[op];
if (gUsePhysicalFiles) {
SkFILEWStream outFile(state.filename);
if (!outFile.isValid()) {
SkASSERT(0);
return;
}
outputToStream(state, pathStr, NULL, nameSuffix, testFunction, outFile);
return;
}
SkFILEWStream outRam(state.filename);
outputToStream(state, pathStr, NULL, nameSuffix, testFunction, outRam);
}
static void writeTestName(const char* nameSuffix, SkWStream& outFile) {
outFile.writeText(testName);
outFile.writeDecAsText(testNumber);
if (nameSuffix) {
outFile.writeText(nameSuffix);
}
}
void outputToStream(const State4& state, const char* pathStr, const char* pathPrefix,
const char* nameSuffix,
const char* testFunction, SkWStream& outFile) {
outFile.writeText("<div id=\"");
writeTestName(nameSuffix, outFile);
outFile.writeText("\">\n");
if (pathPrefix) {
outFile.writeText(pathPrefix);
}
outFile.writeText(pathStr);
outFile.writeText("</div>\n\n");
outFile.writeText(marker);
outFile.writeText(" ");
writeTestName(nameSuffix, outFile);
outFile.writeText(",\n\n\n");
outFile.writeText("static void ");
writeTestName(nameSuffix, outFile);
outFile.writeText("() {\n SkPath path");
if (!pathPrefix) {
outFile.writeText(", pathB");
}
outFile.writeText(";\n");
if (pathPrefix) {
outFile.writeText(pathPrefix);
}
outFile.writeText(pathStr);
outFile.writeText(" ");
outFile.writeText(testFunction);
outFile.writeText("\n}\n\n");
outFile.writeText("static void (*firstTest)() = ");
writeTestName(nameSuffix, outFile);
outFile.writeText(";\n\n");
outFile.writeText("static struct {\n");
outFile.writeText(" void (*fun)();\n");
outFile.writeText(" const char* str;\n");
outFile.writeText("} tests[] = {\n");
outFile.writeText(" TEST(");
writeTestName(nameSuffix, outFile);
outFile.writeText("),\n");
outFile.flush();
}
bool runNextTestSet(State4& state) {
if (gRunTestsInOneThread) {
return false;
}
pthread_mutex_lock(&State4::addQueue);
state.done = true;
State4::queue = &state;
if (debugThreads) SkDebugf("%s %d checkQueue done=%d last=%d\n", __FUNCTION__, state.index,
state.done, state.last);
pthread_cond_signal(&State4::checkQueue);
while (state.done && !state.last) {
if (debugThreads) SkDebugf("%s %d done=%d last=%d\n", __FUNCTION__, state.index, state.done, state.last);
pthread_cond_wait(&state.initialized, &State4::addQueue);
}
pthread_mutex_unlock(&State4::addQueue);
return !state.last;
}
int waitForCompletion() {
int testsRun = 0;
if (!gRunTestsInOneThread) {
pthread_mutex_lock(&State4::addQueue);
int runningThreads = maxThreads;
int index;
while (runningThreads > 0) {
while (!State4::queue) {
if (debugThreads) SkDebugf("%s checkQueue\n", __FUNCTION__);
pthread_cond_wait(&State4::checkQueue, &State4::addQueue);
}
while (State4::queue) {
--runningThreads;
SkDebugf("");
State4::queue->last = true;
State4* next = NULL;
for (index = 0; index < maxThreads; ++index) {
State4& test = threadState[index];
if (test.done && !test.last) {
next = &test;
}
}
if (debugThreads) SkDebugf("%s %d next=%d deQueue\n", __FUNCTION__,
State4::queue->index, next ? next->index : -1);
pthread_cond_signal(&State4::queue->initialized);
State4::queue = next;
}
}
pthread_mutex_unlock(&State4::addQueue);
for (index = 0; index < maxThreads; ++index) {
pthread_join(threadState[index].threadID, NULL);
testsRun += threadState[index].testsRun;
}
SkDebugf("\n");
}
#ifdef SK_DEBUG
gDebugMaxWindSum = SK_MaxS32;
gDebugMaxWindValue = SK_MaxS32;
#endif
return testsRun;
}

View File

@ -1,2 +0,0 @@
/* Localized versions of Info.plist keys */

File diff suppressed because it is too large Load Diff

View File

@ -1,81 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
#include "Extrema.h"
static int validUnitDivide(double numer, double denom, double* ratio)
{
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
return 0;
}
*ratio = r;
return 1;
}
/** From Numerical Recipes in C.
Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C])
x1 = Q / A
x2 = C / Q
*/
static int findUnitQuadRoots(double A, double B, double C, double roots[2])
{
if (A == 0)
return validUnitDivide(-C, B, roots);
double* r = roots;
double R = B*B - 4*A*C;
if (R < 0) { // complex roots
return 0;
}
R = sqrt(R);
double Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
r += validUnitDivide(Q, A, r);
r += validUnitDivide(C, Q, r);
if (r - roots == 2 && AlmostEqualUlps(roots[0], roots[1])) { // nearly-equal?
r -= 1; // skip the double root
}
return (int)(r - roots);
}
/** Cubic'(t) = At^2 + Bt + C, where
A = 3(-a + 3(b - c) + d)
B = 6(a - 2b + c)
C = 3(b - a)
Solve for t, keeping only those that fit between 0 < t < 1
*/
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 findUnitQuadRoots(A, B, C, tValues);
}
/** Quad'(t) = At + B, where
A = 2(a - 2b + c)
B = 2(b - a)
Solve for t, only if it fits between 0 < t < 1
*/
int findExtrema(double a, double b, double c, double tValue[1])
{
/* At + B == 0
t = -B / A
*/
return validUnitDivide(a - b, a - b - b + c, tValue);
}

View File

@ -1,8 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
int findExtrema(double a, double b, double c, double d, double tValues[2]);
int findExtrema(double a, double b, double c, double tValue[1]);

View File

@ -1,54 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Intersection_Tests.h"
#include "IntersectionUtilities.h"
static void assert_that(int x, int y, const char* s) {
if (x == y) {
return;
}
SkDebugf("result=%d expected=%d %s\n", x, y, s);
}
static void side_test() {
assert_that(side(-1), 0, "side(-1) != 0");
assert_that(side(0), 1, "side(0) != 1");
assert_that(side(1), 2, "side(1) != 2");
}
static void sideBit_test() {
assert_that(sideBit(-1), 1, "sideBit(-1) != 1");
assert_that(sideBit(0), 2, "sideBit(0) != 2");
assert_that(sideBit(1), 4, "sideBit(1) != 4");
}
static void other_two_test() {
for (int x = 0; x < 4; ++x) {
for (int y = 0; y < 4; ++y) {
if (x == y) {
continue;
}
int mask = other_two(x, y);
int all = 1 << x;
all |= 1 << y;
all |= 1 << (x ^ mask);
all |= 1 << (y ^ mask);
if (all == 0x0F) {
continue;
}
SkDebugf("[%d,%d] other_two failed mask=%d [%d,%d]\n",
x, y, mask, x ^ mask, y ^ mask);
}
}
}
void Inline_Tests() {
side_test();
sideBit_test();
other_two_test();
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#if 0
// snippets that one day may be useful, unused for now...
// get sign, exponent, mantissa from double
// Translate the double into sign, exponent and mantissa.
long bits = BitConverter.DoubleToInt64Bits(d);
// Note that the shift is sign-extended, hence the test against -1 not 1
bool negative = (bits < 0);
int exponent = (int) ((bits >> 52) & 0x7ffL);
long mantissa = bits & 0xfffffffffffffL;
// Subnormal numbers; exponent is effectively one higher,
// but there's no extra normalisation bit in the mantissa
if (exponent==0)
{
exponent++;
}
// Normal numbers; leave exponent as it is but add extra
// bit to the front of the mantissa
else
{
mantissa = mantissa | (1L<<52);
}
// Bias the exponent. It's actually biased by 1023, but we're
// treating the mantissa as m.0 rather than 0.m, so we need
// to subtract another 52 from it.
exponent -= 1075;
if (mantissa == 0)
{
return "0";
}
/* Normalize */
while((mantissa & 1) == 0)
{ /* i.e., Mantissa is even */
mantissa >>= 1;
exponent++;
}
#endif

View File

@ -1,47 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
// inline utilities
/* Returns 0 if negative, 1 if zero, 2 if positive
*/
inline int side(double x) {
return (x > 0) + (x >= 0);
}
/* Returns 1 if negative, 2 if zero, 4 if positive
*/
inline int sideBit(double x) {
return 1 << side(x);
}
/* Given the set [0, 1, 2, 3], and two of the four members, compute an XOR mask
that computes the other two. Note that:
one ^ two == 3 for (0, 3), (1, 2)
one ^ two < 3 for (0, 1), (0, 2), (1, 3), (2, 3)
3 - (one ^ two) is either 0, 1, or 2
1 >> 3 - (one ^ two) is either 0 or 1
thus:
returned == 2 for (0, 3), (1, 2)
returned == 3 for (0, 1), (0, 2), (1, 3), (2, 3)
given that:
(0, 3) ^ 2 -> (2, 1) (1, 2) ^ 2 -> (3, 0)
(0, 1) ^ 3 -> (3, 2) (0, 2) ^ 3 -> (3, 1) (1, 3) ^ 3 -> (2, 0) (2, 3) ^ 3 -> (1, 0)
*/
inline int other_two(int one, int two) {
return 1 >> 3 - (one ^ two) ^ 3;
}
/* Returns -1 if negative, 0 if zero, 1 if positive
*/
inline int sign(double x) {
return (x > 0) - (x < 0);
}
inline double interp(double A, double B, double t) {
return A + (B - A) * t;
}

View File

@ -1,84 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CubicIntersection_TestData.h"
#include "Intersection_Tests.h"
void cubecode_test(int test);
void parseSVG();
#define TEST_QUADS_FIRST 0
void Intersection_Tests() {
int testsRun = 0;
#if 0
CubicIntersection_OneOffTest();
CubicIntersection_SelfTest();
QuadraticIntersection_IntersectionFinder();
QuadraticIntersection_OneOffTest();
CubicIntersection_IntersectionFinder();
CubicUtilities_Test();
#endif
SimplifyNew_Test();
CubicsToQuadratics_OneOffTest();
CubicIntersection_OneOffTest();
// CubicIntersection_OneOffTests();
#if 0
parseSVG();
#endif
// QuadraticIntersection_PointFinder();
ShapeOps4x4CubicsThreaded_Test(testsRun);
CubicToQuadratics_Test();
QuadraticIntersection_Test();
QuarticRoot_Test();
CubicIntersection_RandTest();
CubicsToQuadratics_RandTest();
Simplify4x4RectsThreaded_Test(testsRun);
Simplify4x4QuadraticsThreaded_Test(testsRun);
QuadLineIntersectThreaded_Test(testsRun);
SimplifyNondegenerate4x4TrianglesThreaded_Test(testsRun);
SimplifyDegenerate4x4TrianglesThreaded_Test(testsRun);
Simplify4x4QuadralateralsThreaded_Test(testsRun);
ShapeOps4x4RectsThreaded_Test(testsRun);
SkDebugf("%s total testsRun=%d\n", __FUNCTION__, testsRun);
LineQuadraticIntersection_Test();
MiniSimplify_Test();
SimplifyAngle_Test();
QuadraticBezierClip_Test();
SimplifyFindNext_Test();
SimplifyFindTop_Test();
QuadraticReduceOrder_Test();
SimplifyAddIntersectingTs_Test();
cubecode_test(1);
convert_testx();
// tests are in dependency / complexity order
Inline_Tests();
ConvexHull_Test();
ConvexHull_X_Test();
LineParameter_Test();
LineIntersection_Test();
LineCubicIntersection_Test();
SimplifyQuadraticPaths_Test();
SimplifyPolygonPaths_Test();
SimplifyRectangularPaths_Test();
SimplifyQuadralateralPaths_Test();
// ActiveEdge_Test();
QuadraticCoincidence_Test();
QuadraticIntersection_Test();
CubicParameterization_Test();
CubicCoincidence_Test();
CubicReduceOrder_Test();
CubicBezierClip_Test();
CubicIntersection_Test();
}

View File

@ -1,61 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes_Test.h"
void ActiveEdge_Test();
void ConvexHull_Test();
void ConvexHull_X_Test();
void CubicBezierClip_Test();
void CubicCoincidence_Test();
void CubicIntersection_IntersectionFinder();
void CubicIntersection_NewOneOffTest();
void CubicIntersection_OneOffTest();
void CubicIntersection_OneOffTests();
void CubicIntersection_SelfTest();
void CubicIntersection_Test();
void CubicIntersection_RandTest();
void CubicIntersection_RandTestOld();
void CubicParameterization_Test();
void CubicReduceOrder_Test();
void CubicsToQuadratics_OneOffTest();
void CubicsToQuadratics_OneOffTests();
void CubicsToQuadratics_RandTest();
void CubicToQuadratics_Test();
void CubicUtilities_Test();
void Inline_Tests();
void Intersection_Tests();
void LineCubicIntersection_Test();
void LineIntersection_Test();
void LineParameter_Test();
void LineQuadraticIntersection_Test();
void MiniSimplify_Test();
void QuadraticIntersection_IntersectionFinder();
void QuadraticIntersection_OneOffTest();
void QuadraticIntersection_PointFinder();
void QuadLineIntersectThreaded_Test(int& );
void QuadraticBezierClip_Test();
void QuadraticCoincidence_Test();
void QuadraticIntersection_Test();
void QuadraticParameterization_Test();
void QuadraticReduceOrder_Test();
void QuarticRoot_Test();
void SimplifyAddIntersectingTs_Test();
void SimplifyAngle_Test();
void SimplifyDegenerate4x4TrianglesThreaded_Test(int& );
void SimplifyFindNext_Test();
void SimplifyFindTop_Test();
void SimplifyNew_Test();
void SimplifyNondegenerate4x4TrianglesThreaded_Test(int& );
void SimplifyPolygonPaths_Test();
void SimplifyQuadralateralPaths_Test();
void SimplifyQuadraticPaths_Test();
void Simplify4x4QuadralateralsThreaded_Test(int& );
void Simplify4x4QuadraticsThreaded_Test(int& );
void Simplify4x4RectsThreaded_Test(int& );
void SimplifyRectangularPaths_Test();
void ShapeOps4x4CubicsThreaded_Test(int& );
void ShapeOps4x4RectsThreaded_Test(int& );

View File

@ -1,158 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
#include "Intersections.h"
void Intersections::insertCoincidentPair(double s1, double e1, double s2, double e2,
const _Point& startPt, const _Point& endPt) {
if (fSwap) {
remove(s2, e2, startPt, endPt);
} else {
remove(s1, e1, startPt, endPt);
}
SkASSERT(coincidentUsed() == fUsed);
SkASSERT((coincidentUsed() & 1) != 1);
int i1 = 0;
int i2 = 0;
do {
while (i1 < fUsed && !(fIsCoincident[fSwap] & (1 << i1))) {
++i1;
}
if (i1 == fUsed) {
break;
}
SkASSERT(i1 < fUsed);
int iEnd1 = i1 + 1;
while (!(fIsCoincident[fSwap] & (1 << iEnd1))) {
++iEnd1;
}
SkASSERT(iEnd1 < fUsed);
double cs1 = fT[fSwap][i1];
double ce1 = fT[fSwap][iEnd1];
bool s1in = between(cs1, s1, ce1) || startPt.approximatelyEqual(fPt[i1])
|| startPt.approximatelyEqual(fPt[iEnd1]);
bool e1in = between(cs1, e1, ce1) || endPt.approximatelyEqual(fPt[i1])
|| endPt.approximatelyEqual(fPt[iEnd1]);
while (i2 < fUsed && !(fIsCoincident[fSwap ^ 1] & (1 << i2))) {
++i2;
}
int iEnd2 = i2 + 1;
while (!(fIsCoincident[fSwap ^ 1] & (1 << iEnd2))) {
++iEnd2;
}
SkASSERT(iEnd2 < fUsed);
double cs2 = fT[fSwap ^ 1][i2];
double ce2 = fT[fSwap ^ 1][iEnd2];
bool s2in = between(cs2, s2, ce2) || startPt.approximatelyEqual(fPt[i2])
|| startPt.approximatelyEqual(fPt[iEnd2]);
bool e2in = between(cs2, e2, ce2) || endPt.approximatelyEqual(fPt[i2])
|| endPt.approximatelyEqual(fPt[iEnd2]);
if ((s1in | e1in) & (s2in | e2in)) {
if (s1 < cs1) {
fT[fSwap][i1] = s1;
fPt[i1] = startPt;
} else if (e1 < cs1) {
fT[fSwap][i1] = e1;
fPt[i1] = endPt;
}
if (s1 > ce1) {
fT[fSwap][iEnd1] = s1;
fPt[iEnd1] = startPt;
} else if (e1 > ce1) {
fT[fSwap][iEnd1] = e1;
fPt[iEnd1] = endPt;
}
if (s2 > e2) {
SkTSwap(cs2, ce2);
SkTSwap(i2, iEnd2);
}
if (s2 < cs2) {
fT[fSwap ^ 1][i2] = s2;
} else if (e2 < cs2) {
fT[fSwap ^ 1][i2] = e2;
}
if (s2 > ce2) {
fT[fSwap ^ 1][iEnd2] = s2;
} else if (e2 > ce2) {
fT[fSwap ^ 1][iEnd2] = e2;
}
return;
}
} while (true);
SkASSERT(fUsed < 9);
insertCoincident(s1, s2, startPt);
insertCoincident(e1, e2, endPt);
}
int Intersections::insert(double one, double two, const _Point& pt) {
SkASSERT(fUsed <= 1 || fT[0][0] <= fT[0][1]);
int index;
for (index = 0; index < fUsed; ++index) {
double oldOne = fT[0][index];
double oldTwo = fT[1][index];
if (roughly_equal(oldOne, one) && roughly_equal(oldTwo, two)) {
if ((precisely_zero(one) && !precisely_zero(oldOne))
|| (precisely_equal(one, 1) && !precisely_equal(oldOne, 1))
|| (precisely_zero(two) && !precisely_zero(oldTwo))
|| (precisely_equal(two, 1) && !precisely_equal(oldTwo, 1))) {
fT[0][index] = one;
fT[1][index] = two;
fPt[index] = pt;
}
return -1;
}
#if ONE_OFF_DEBUG
if (pt.roughlyEqual(fPt[index])) {
SkDebugf("%s t=%1.9g pts roughly equal\n", __FUNCTION__, one);
}
#endif
if (fT[0][index] > one) {
break;
}
}
SkASSERT(fUsed < 9);
int remaining = fUsed - index;
if (remaining > 0) {
memmove(&fPt[index + 1], &fPt[index], sizeof(fPt[0]) * remaining);
memmove(&fT[0][index + 1], &fT[0][index], sizeof(fT[0][0]) * remaining);
memmove(&fT[1][index + 1], &fT[1][index], sizeof(fT[1][0]) * remaining);
fIsCoincident[0] += fIsCoincident[0] & ~((1 << index) - 1);
fIsCoincident[1] += fIsCoincident[1] & ~((1 << index) - 1);
}
fPt[index] = pt;
fT[0][index] = one;
fT[1][index] = two;
++fUsed;
return index;
}
void Intersections::remove(double one, double two, const _Point& startPt, const _Point& endPt) {
for (int index = fUsed - 1; index >= 0; --index) {
if (!(fIsCoincident[0] & (1 << index)) && (between(one, fT[fSwap][index], two)
|| startPt.approximatelyEqual(fPt[index])
|| endPt.approximatelyEqual(fPt[index]))) {
SkASSERT(fUsed > 0);
removeOne(index);
}
}
}
void Intersections::removeOne(int index) {
int remaining = --fUsed - index;
if (remaining <= 0) {
return;
}
memmove(&fPt[index], &fPt[index + 1], sizeof(fPt[0]) * remaining);
memmove(&fT[0][index], &fT[0][index + 1], sizeof(fT[0][0]) * remaining);
memmove(&fT[1][index], &fT[1][index + 1], sizeof(fT[1][0]) * remaining);
SkASSERT(fIsCoincident[0] == 0);
int coBit = fIsCoincident[0] & (1 << index);
fIsCoincident[0] -= ((fIsCoincident[0] >> 1) & ~((1 << index) - 1)) + coBit;
SkASSERT(!(coBit ^ (fIsCoincident[1] & (1 << index))));
fIsCoincident[1] -= ((fIsCoincident[1] >> 1) & ~((1 << index) - 1)) + coBit;
}

View File

@ -1,144 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef Intersections_DEFINE
#define Intersections_DEFINE
class Intersections {
public:
Intersections()
: fFlip(0)
#ifdef SK_DEBUG
, fDepth(0)
#endif
, fSwap(0)
{
#ifdef SK_DEBUG
bzero(fPt, sizeof(fPt));
bzero(fT, sizeof(fT));
bzero(fIsCoincident, sizeof(fIsCoincident));
#endif
reset();
}
int coincidentUsed() const {
if (!fIsCoincident[0]) {
SkASSERT(!fIsCoincident[0]);
return 0;
}
int count = 0;
SkDEBUGCODE(int count2 = 0;)
for (int index = 0; index < fUsed; ++index) {
if (fIsCoincident[0] & (1 << index)) {
++count;
}
#ifdef SK_DEBUG
if (fIsCoincident[1] & (1 << index)) {
++count2;
}
#endif
}
SkASSERT(count == count2);
return count;
}
void offset(int base, double start, double end) {
for (int index = base; index < fUsed; ++index) {
double val = fT[fSwap][index];
val *= end - start;
val += start;
fT[fSwap][index] = val;
}
}
// FIXME : does not respect swap
int insert(double one, double two, const _Point& pt);
// start if index == 0 : end if index == 1
void insertCoincident(double one, double two, const _Point& pt) {
int index = insertSwap(one, two, pt);
int bit = 1 << index;
fIsCoincident[0] |= bit;
fIsCoincident[1] |= bit;
}
void insertCoincidentPair(double s1, double e1, double s2, double e2,
const _Point& startPt, const _Point& endPt);
int insertSwap(double one, double two, const _Point& pt) {
if (fSwap) {
return insert(two, one, pt);
} else {
return insert(one, two, pt);
}
}
bool intersected() const {
return fUsed > 0;
}
void removeOne(int index);
// leaves flip, swap alone
void reset() {
fUsed = 0;
fUnsortable = false;
}
void swap() {
fSwap ^= true;
}
void swapPts() {
int index;
for (index = 0; index < fUsed; ++index) {
SkTSwap(fT[0][index], fT[1][index]);
}
}
bool swapped() const {
return fSwap;
}
bool unsortable() const {
return fUnsortable;
}
int used() const {
return fUsed;
}
void downDepth() {
SkASSERT(--fDepth >= 0);
}
void upDepth() {
SkASSERT(++fDepth < 16);
}
#ifdef SK_DEBUG
int depth() const {
return fDepth;
}
#endif
_Point fPt[9];
double fT[2][9];
unsigned short fIsCoincident[2]; // bit arrays, one bit set for each coincident T
unsigned char fUsed;
bool fFlip;
bool fUnsortable;
#ifdef SK_DEBUG
int fDepth;
#endif
protected:
// used by addCoincident to remove ordinary intersections in range
void remove(double one, double two, const _Point& startPt, const _Point& endPt);
private:
bool fSwap;
};
#endif

View File

@ -1,296 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CubicUtilities.h"
#include "Intersections.h"
#include "LineUtilities.h"
/*
Find the interection of a line and cubic by solving for valid t values.
Analogous to line-quadratic intersection, solve line-cubic intersection by
representing the cubic as:
x = a(1-t)^3 + 2b(1-t)^2t + c(1-t)t^2 + dt^3
y = e(1-t)^3 + 2f(1-t)^2t + g(1-t)t^2 + ht^3
and the line as:
y = i*x + j (if the line is more horizontal)
or:
x = i*y + j (if the line is more vertical)
Then using Mathematica, solve for the values of t where the cubic intersects the
line:
(in) Resultant[
a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - x,
e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - i*x - j, x]
(out) -e + j +
3 e t - 3 f t -
3 e t^2 + 6 f t^2 - 3 g t^2 +
e t^3 - 3 f t^3 + 3 g t^3 - h t^3 +
i ( a -
3 a t + 3 b t +
3 a t^2 - 6 b t^2 + 3 c t^2 -
a t^3 + 3 b t^3 - 3 c t^3 + d t^3 )
if i goes to infinity, we can rewrite the line in terms of x. Mathematica:
(in) Resultant[
a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - i*y - j,
e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y]
(out) a - j -
3 a t + 3 b t +
3 a t^2 - 6 b t^2 + 3 c t^2 -
a t^3 + 3 b t^3 - 3 c t^3 + d t^3 -
i ( e -
3 e t + 3 f t +
3 e t^2 - 6 f t^2 + 3 g t^2 -
e t^3 + 3 f t^3 - 3 g t^3 + h t^3 )
Solving this with Mathematica produces an expression with hundreds of terms;
instead, use Numeric Solutions recipe to solve the cubic.
The near-horizontal case, in terms of: Ax^3 + Bx^2 + Cx + D == 0
A = (-(-e + 3*f - 3*g + h) + i*(-a + 3*b - 3*c + d) )
B = 3*(-( e - 2*f + g ) + i*( a - 2*b + c ) )
C = 3*(-(-e + f ) + i*(-a + b ) )
D = (-( e ) + i*( a ) + j )
The near-vertical case, in terms of: Ax^3 + Bx^2 + Cx + D == 0
A = ( (-a + 3*b - 3*c + d) - i*(-e + 3*f - 3*g + h) )
B = 3*( ( a - 2*b + c ) - i*( e - 2*f + g ) )
C = 3*( (-a + b ) - i*(-e + f ) )
D = ( ( a ) - i*( e ) - j )
For horizontal lines:
(in) Resultant[
a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - j,
e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y]
(out) e - j -
3 e t + 3 f t +
3 e t^2 - 6 f t^2 + 3 g t^2 -
e t^3 + 3 f t^3 - 3 g t^3 + h t^3
So the cubic coefficients are:
*/
class LineCubicIntersections {
public:
LineCubicIntersections(const Cubic& c, const _Line& l, Intersections& i)
: cubic(c)
, line(l)
, intersections(i) {
}
// see parallel routine in line quadratic intersections
int intersectRay(double roots[3]) {
double adj = line[1].x - line[0].x;
double opp = line[1].y - line[0].y;
Cubic r;
for (int n = 0; n < 4; ++n) {
r[n].x = (cubic[n].y - line[0].y) * adj - (cubic[n].x - line[0].x) * opp;
}
double A, B, C, D;
coefficients(&r[0].x, A, B, C, D);
return cubicRootsValidT(A, B, C, D, roots);
}
int intersect() {
addEndPoints();
double rootVals[3];
int roots = intersectRay(rootVals);
for (int index = 0; index < roots; ++index) {
double cubicT = rootVals[index];
double lineT = findLineT(cubicT);
if (pinTs(cubicT, lineT)) {
_Point pt;
xy_at_t(line, lineT, pt.x, pt.y);
intersections.insert(cubicT, lineT, pt);
}
}
return intersections.fUsed;
}
int horizontalIntersect(double axisIntercept, double roots[3]) {
double A, B, C, D;
coefficients(&cubic[0].y, A, B, C, D);
D -= axisIntercept;
return cubicRootsValidT(A, B, C, D, roots);
}
int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) {
addHorizontalEndPoints(left, right, axisIntercept);
double rootVals[3];
int roots = horizontalIntersect(axisIntercept, rootVals);
for (int index = 0; index < roots; ++index) {
_Point pt;
double cubicT = rootVals[index];
xy_at_t(cubic, cubicT, pt.x, pt.y);
double lineT = (pt.x - left) / (right - left);
if (pinTs(cubicT, lineT)) {
intersections.insert(cubicT, lineT, pt);
}
}
if (flipped) {
flip();
}
return intersections.fUsed;
}
int verticalIntersect(double axisIntercept, double roots[3]) {
double A, B, C, D;
coefficients(&cubic[0].x, A, B, C, D);
D -= axisIntercept;
return cubicRootsValidT(A, B, C, D, roots);
}
int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) {
addVerticalEndPoints(top, bottom, axisIntercept);
double rootVals[3];
int roots = verticalIntersect(axisIntercept, rootVals);
for (int index = 0; index < roots; ++index) {
_Point pt;
double cubicT = rootVals[index];
xy_at_t(cubic, cubicT, pt.x, pt.y);
double lineT = (pt.y - top) / (bottom - top);
if (pinTs(cubicT, lineT)) {
intersections.insert(cubicT, lineT, pt);
}
}
if (flipped) {
flip();
}
return intersections.fUsed;
}
protected:
void addEndPoints()
{
for (int cIndex = 0; cIndex < 4; cIndex += 3) {
for (int lIndex = 0; lIndex < 2; lIndex++) {
if (cubic[cIndex] == line[lIndex]) {
intersections.insert(cIndex >> 1, lIndex, line[lIndex]);
}
}
}
}
void addHorizontalEndPoints(double left, double right, double y)
{
for (int cIndex = 0; cIndex < 4; cIndex += 3) {
if (cubic[cIndex].y != y) {
continue;
}
if (cubic[cIndex].x == left) {
intersections.insert(cIndex >> 1, 0, cubic[cIndex]);
}
if (cubic[cIndex].x == right) {
intersections.insert(cIndex >> 1, 1, cubic[cIndex]);
}
}
}
void addVerticalEndPoints(double top, double bottom, double x)
{
for (int cIndex = 0; cIndex < 4; cIndex += 3) {
if (cubic[cIndex].x != x) {
continue;
}
if (cubic[cIndex].y == top) {
intersections.insert(cIndex >> 1, 0, cubic[cIndex]);
}
if (cubic[cIndex].y == bottom) {
intersections.insert(cIndex >> 1, 1, cubic[cIndex]);
}
}
}
double findLineT(double t) {
double x, y;
xy_at_t(cubic, t, x, y);
double dx = line[1].x - line[0].x;
double dy = line[1].y - line[0].y;
if (fabs(dx) > fabs(dy)) {
return (x - line[0].x) / dx;
}
return (y - line[0].y) / dy;
}
void flip() {
// OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
int roots = intersections.fUsed;
for (int index = 0; index < roots; ++index) {
intersections.fT[1][index] = 1 - intersections.fT[1][index];
}
}
static bool pinTs(double& cubicT, double& lineT) {
if (!approximately_one_or_less(lineT)) {
return false;
}
if (!approximately_zero_or_more(lineT)) {
return false;
}
if (precisely_less_than_zero(cubicT)) {
cubicT = 0;
} else if (precisely_greater_than_one(cubicT)) {
cubicT = 1;
}
if (precisely_less_than_zero(lineT)) {
lineT = 0;
} else if (precisely_greater_than_one(lineT)) {
lineT = 1;
}
return true;
}
private:
const Cubic& cubic;
const _Line& line;
Intersections& intersections;
};
int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
double tRange[3]) {
LineCubicIntersections c(cubic, *((_Line*) 0), *((Intersections*) 0));
double rootVals[3];
int result = c.horizontalIntersect(y, rootVals);
int tCount = 0;
for (int index = 0; index < result; ++index) {
double x, y;
xy_at_t(cubic, rootVals[index], x, y);
if (x < left || x > right) {
continue;
}
tRange[tCount++] = rootVals[index];
}
return result;
}
int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
bool flipped, Intersections& intersections) {
LineCubicIntersections c(cubic, *((_Line*) 0), intersections);
return c.horizontalIntersect(y, left, right, flipped);
}
int verticalIntersect(const Cubic& cubic, double top, double bottom, double x,
bool flipped, Intersections& intersections) {
LineCubicIntersections c(cubic, *((_Line*) 0), intersections);
return c.verticalIntersect(x, top, bottom, flipped);
}
int intersect(const Cubic& cubic, const _Line& line, Intersections& i) {
LineCubicIntersections c(cubic, line, i);
return c.intersect();
}
int intersectRay(const Cubic& cubic, const _Line& line, Intersections& i) {
LineCubicIntersections c(cubic, line, i);
return c.intersectRay(i.fT[0]);
}

View File

@ -1,62 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Intersection_Tests.h"
#include "Intersections.h"
#include "TestUtilities.h"
struct lineCubic {
Cubic cubic;
_Line line;
} lineCubicTests[] = {
{{{0, 0}, {0, 1}, {0, 1}, {1, 1}}, {{0, 1}, {1, 0}}}
};
size_t lineCubicTests_count = sizeof(lineCubicTests) / sizeof(lineCubicTests[0]);
const int firstLineCubicIntersectionTest = 0;
void LineCubicIntersection_Test() {
for (size_t index = firstLineCubicIntersectionTest; index < lineCubicTests_count; ++index) {
const Cubic& cubic = lineCubicTests[index].cubic;
const _Line& line = lineCubicTests[index].line;
Cubic reduce1;
_Line reduce2;
int order1 = reduceOrder(cubic, reduce1, kReduceOrder_NoQuadraticsAllowed,
kReduceOrder_TreatAsFill);
int order2 = reduceOrder(line, reduce2);
if (order1 < 4) {
printf("[%d] cubic order=%d\n", (int) index, order1);
}
if (order2 < 2) {
printf("[%d] line order=%d\n", (int) index, order2);
}
if (order1 == 4 && order2 == 2) {
Intersections i;
double* range1 = i.fT[0];
double* range2 = i.fT[1];
int roots = intersect(reduce1, reduce2, i);
for (int pt = 0; pt < roots; ++pt) {
double tt1 = range1[pt];
double tx1, ty1;
xy_at_t(cubic, tt1, tx1, ty1);
double tt2 = range2[pt];
double tx2, ty2;
xy_at_t(line, tt2, tx2, ty2);
if (!AlmostEqualUlps(tx1, tx2)) {
printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
__FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
}
if (!AlmostEqualUlps(ty1, ty2)) {
printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
__FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
}
}
}
}
}

View File

@ -1,338 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Intersections.h"
#include "LineIntersection.h"
#include "LineUtilities.h"
/* Determine the intersection point of two lines. This assumes the lines are not parallel,
and that that the lines are infinite.
From http://en.wikipedia.org/wiki/Line-line_intersection
*/
void lineIntersect(const _Line& a, const _Line& b, _Point& p) {
double axLen = a[1].x - a[0].x;
double ayLen = a[1].y - a[0].y;
double bxLen = b[1].x - b[0].x;
double byLen = b[1].y - b[0].y;
double denom = byLen * axLen - ayLen * bxLen;
SkASSERT(denom);
double term1 = a[1].x * a[0].y - a[1].y * a[0].x;
double term2 = b[1].x * b[0].y - b[1].y * b[0].x;
p.x = (term1 * bxLen - axLen * term2) / denom;
p.y = (term1 * byLen - ayLen * term2) / denom;
}
static int computePoints(const _Line& a, int used, Intersections& i) {
i.fPt[0] = xy_at_t(a, i.fT[0][0]);
if ((i.fUsed = used) == 2) {
i.fPt[1] = xy_at_t(a, i.fT[0][1]);
}
return i.fUsed;
}
/*
Determine the intersection point of two line segments
Return FALSE if the lines don't intersect
from: http://paulbourke.net/geometry/lineline2d/
*/
int intersect(const _Line& a, const _Line& b, Intersections& i) {
double axLen = a[1].x - a[0].x;
double ayLen = a[1].y - a[0].y;
double bxLen = b[1].x - b[0].x;
double byLen = b[1].y - b[0].y;
/* Slopes match when denom goes to zero:
axLen / ayLen == bxLen / byLen
(ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
byLen * axLen == ayLen * bxLen
byLen * axLen - ayLen * bxLen == 0 ( == denom )
*/
double denom = byLen * axLen - ayLen * bxLen;
double ab0y = a[0].y - b[0].y;
double ab0x = a[0].x - b[0].x;
double numerA = ab0y * bxLen - byLen * ab0x;
double numerB = ab0y * axLen - ayLen * ab0x;
bool mayNotOverlap = (numerA < 0 && denom > numerA) || (numerA > 0 && denom < numerA)
|| (numerB < 0 && denom > numerB) || (numerB > 0 && denom < numerB);
numerA /= denom;
numerB /= denom;
if ((!approximately_zero(denom) || (!approximately_zero_inverse(numerA)
&& !approximately_zero_inverse(numerB))) && !sk_double_isnan(numerA)
&& !sk_double_isnan(numerB)) {
if (mayNotOverlap) {
return 0;
}
i.fT[0][0] = numerA;
i.fT[1][0] = numerB;
i.fPt[0] = xy_at_t(a, numerA);
return computePoints(a, 1, i);
}
/* See if the axis intercepts match:
ay - ax * ayLen / axLen == by - bx * ayLen / axLen
axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
axLen * ay - ax * ayLen == axLen * by - bx * ayLen
*/
// FIXME: need to use AlmostEqualUlps variant instead
if (!approximately_equal_squared(axLen * a[0].y - ayLen * a[0].x,
axLen * b[0].y - ayLen * b[0].x)) {
return 0;
}
const double* aPtr;
const double* bPtr;
if (fabs(axLen) > fabs(ayLen) || fabs(bxLen) > fabs(byLen)) {
aPtr = &a[0].x;
bPtr = &b[0].x;
} else {
aPtr = &a[0].y;
bPtr = &b[0].y;
}
double a0 = aPtr[0];
double a1 = aPtr[2];
double b0 = bPtr[0];
double b1 = bPtr[2];
// OPTIMIZATION: restructure to reject before the divide
// e.g., if ((a0 - b0) * (a0 - a1) < 0 || abs(a0 - b0) > abs(a0 - a1))
// (except efficient)
double aDenom = a0 - a1;
if (approximately_zero(aDenom)) {
if (!between(b0, a0, b1)) {
return 0;
}
i.fT[0][0] = i.fT[0][1] = 0;
} else {
double at0 = (a0 - b0) / aDenom;
double at1 = (a0 - b1) / aDenom;
if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
return 0;
}
i.fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
i.fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
}
double bDenom = b0 - b1;
if (approximately_zero(bDenom)) {
i.fT[1][0] = i.fT[1][1] = 0;
} else {
int bIn = aDenom * bDenom < 0;
i.fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / bDenom, 1.0), 0.0);
i.fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / bDenom, 1.0), 0.0);
}
bool second = fabs(i.fT[0][0] - i.fT[0][1]) > FLT_EPSILON;
SkASSERT((fabs(i.fT[1][0] - i.fT[1][1]) <= FLT_EPSILON) ^ second);
return computePoints(a, 1 + second, i);
}
int horizontalIntersect(const _Line& line, double y, double tRange[2]) {
double min = line[0].y;
double max = line[1].y;
if (min > max) {
SkTSwap(min, max);
}
if (min > y || max < y) {
return 0;
}
if (AlmostEqualUlps(min, max)) {
tRange[0] = 0;
tRange[1] = 1;
return 2;
}
tRange[0] = (y - line[0].y) / (line[1].y - line[0].y);
return 1;
}
// OPTIMIZATION Given: dy = line[1].y - line[0].y
// and: xIntercept / (y - line[0].y) == (line[1].x - line[0].x) / dy
// then: xIntercept * dy == (line[1].x - line[0].x) * (y - line[0].y)
// Assuming that dy is always > 0, the line segment intercepts if:
// left * dy <= xIntercept * dy <= right * dy
// thus: left * dy <= (line[1].x - line[0].x) * (y - line[0].y) <= right * dy
// (clever as this is, it does not give us the t value, so may be useful only
// as a quick reject -- and maybe not then; it takes 3 muls, 3 adds, 2 cmps)
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;
}
double xIntercept = line[0].x + tRange[0] * (line[1].x - line[0].x);
if (xIntercept > right || xIntercept < left) {
return 0;
}
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:
#if 0 // sorting edges fails to preserve original direction
double lineL = line[0].x;
double lineR = line[1].x;
if (lineL > lineR) {
SkTSwap(lineL, lineR);
}
double overlapL = SkTMax(left, lineL);
double overlapR = SkTMin(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);
}
#else
double a0 = line[0].x;
double a1 = line[1].x;
double b0 = flipped ? right : left;
double b1 = flipped ? left : right;
// FIXME: share common code below
double at0 = (a0 - b0) / (a0 - a1);
double at1 = (a0 - b1) / (a0 - a1);
if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
return 0;
}
intersections.fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
intersections.fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
int bIn = (a0 - a1) * (b0 - b1) < 0;
intersections.fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1),
1.0), 0.0);
intersections.fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1),
1.0), 0.0);
bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
> FLT_EPSILON;
SkASSERT((fabs(intersections.fT[1][0] - intersections.fT[1][1])
<= FLT_EPSILON) ^ second);
return computePoints(line, 1 + second, intersections);
#endif
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 computePoints(line, result, intersections);
}
static int verticalIntersect(const _Line& line, double x, double tRange[2]) {
double min = line[0].x;
double max = line[1].x;
if (min > max) {
SkTSwap(min, max);
}
if (min > x || max < x) {
return 0;
}
if (AlmostEqualUlps(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:
#if 0 // sorting edges fails to preserve original direction
double lineT = line[0].y;
double lineB = line[1].y;
if (lineT > lineB) {
SkTSwap(lineT, lineB);
}
double overlapT = SkTMax(top, lineT);
double overlapB = SkTMin(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);
}
#else
double a0 = line[0].y;
double a1 = line[1].y;
double b0 = flipped ? bottom : top;
double b1 = flipped ? top : bottom;
// FIXME: share common code above
double at0 = (a0 - b0) / (a0 - a1);
double at1 = (a0 - b1) / (a0 - a1);
if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
return 0;
}
intersections.fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
intersections.fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
int bIn = (a0 - a1) * (b0 - b1) < 0;
intersections.fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1),
1.0), 0.0);
intersections.fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1),
1.0), 0.0);
bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
> FLT_EPSILON;
SkASSERT((fabs(intersections.fT[1][0] - intersections.fT[1][1])
<= FLT_EPSILON) ^ second);
return computePoints(line, 1 + second, intersections);
#endif
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 computePoints(line, result, intersections);
}
// 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) {
return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x);
}
// 16 subs, 8 muls, 6 cmps
bool testIntersect(const _Line& a, const _Line& b) {
return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
&& ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
}

View File

@ -1,21 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef LineIntersection_DEFINE
#define LineIntersection_DEFINE
#include "Intersections.h"
int horizontalIntersect(const _Line& line, double y, double tRange[2]);
int horizontalLineIntersect(const _Line& line, double left, double right,
double y, double tRange[2]);
void lineIntersect(const _Line& a, const _Line& b, _Point& p);
int intersect(const _Line& a, const _Line& b, Intersections&);
bool testIntersect(const _Line& a, const _Line& b);
int verticalLineIntersect(const _Line& line, double top, double bottom,
double x, double tRange[2]);
#endif

View File

@ -1,72 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveUtilities.h"
#include "Intersection_Tests.h"
#include "LineIntersection.h"
// FIXME: add tests for intersecting, non-intersecting, degenerate, coincident
const _Line tests[][2] = {
{{{0, 0}, {1, 0}}, {{1, 0}, {0, 0}}},
{{{0, 0}, {0, 0}}, {{0, 0}, {1, 0}}},
{{{0, 1}, {0, 1}}, {{0, 0}, {0, 2}}},
{{{0, 0}, {1, 0}}, {{0, 0}, {2, 0}}},
{{{1, 1}, {2, 2}}, {{0, 0}, {3, 3}}},
{{{166.86950047022856, 112.69654129527828}, {166.86948801592692, 112.69655741235339}},
{{166.86960700313026, 112.6965477747386}, {166.86925794355412, 112.69656471103423}}}
};
const size_t tests_count = sizeof(tests) / sizeof(tests[0]);
const _Line noIntersect[][2] = {
{{{0, 0}, {1, 0}}, {{3, 0}, {2, 0}}},
{{{0, 0}, {0, 0}}, {{1, 0}, {2, 0}}},
{{{0, 1}, {0, 1}}, {{0, 3}, {0, 2}}},
{{{0, 0}, {1, 0}}, {{2, 0}, {3, 0}}},
{{{1, 1}, {2, 2}}, {{4, 4}, {3, 3}}},
};
const size_t noIntersect_count = sizeof(noIntersect) / sizeof(noIntersect[0]);
static size_t firstLineIntersectionTest = 0;
static size_t firstNoIntersectionTest = 0;
void LineIntersection_Test() {
size_t index;
for (index = firstLineIntersectionTest; index < tests_count; ++index) {
const _Line& line1 = tests[index][0];
const _Line& line2 = tests[index][1];
Intersections ts;
int pts = intersect(line1, line2, ts);
if (!pts) {
printf("%s [%zu] no intersection found\n", __FUNCTION__, index);
}
for (int i = 0; i < pts; ++i) {
_Point result1, result2;
xy_at_t(line1, ts.fT[0][i], result1.x, result1.y);
xy_at_t(line2, ts.fT[1][i], result2.x, result2.y);
if (!result1.approximatelyEqual(result2)) {
if (pts == 1) {
printf("%s [%zu] not equal\n", __FUNCTION__, index);
} else {
xy_at_t(line2, ts.fT[1][i ^ 1], result2.x, result2.y);
if (!result1.approximatelyEqual(result2)) {
printf("%s [%zu] not equal\n", __FUNCTION__, index);
}
}
}
}
}
for (index = firstNoIntersectionTest; index < noIntersect_count; ++index) {
const _Line& line1 = noIntersect[index][0];
const _Line& line2 = noIntersect[index][1];
Intersections ts;
int pts = intersect(line1, line2, ts);
if (pts) {
printf("%s [%zu] no intersection expected\n", __FUNCTION__, index);
}
}
}

View File

@ -1,63 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
/* This rejects coincidence with two muls, two adds, and one cmp.
Coincident candidates will take another four muls and two adds, but may still
fail if they don't overlap. (The overlap test isn't performed here.)
*/
bool implicit_matches(const _Line& one, const _Line& two) {
_Point oneD, twoD;
tangent(one, oneD);
tangent(two, twoD);
/* See if the slopes match, i.e.
dx1 / dy1 == dx2 / dy2
(dy1 * dy2) * dx1 / dy1 == (dy1 * dy2) * dx2 / dy2
dy2 * dx1 == dy1 * dx2
*/
if (!AlmostEqualUlps(oneD.x * twoD.y, twoD.x * oneD.y)) {
return false;
}
/* See if the axis intercepts match, i.e.
y0 - x0 * dy / dx == y1 - x1 * dy / dx
dx * (y0 - x0 * dy / dx) == dx * (y1 - x1 * dy / dx)
dx * y0 - x0 * dy == dx * y1 - x1 * dy
*/
if (!AlmostEqualUlps(oneD.x * one[0].y - oneD.y * one[0].x,
oneD.x * two[0].y - oneD.y * two[0].x)) {
return false;
}
return true;
}
bool implicit_matches_ulps(const _Line& one, const _Line& two, int ulps) {
_Point oneD, twoD;
tangent(one, oneD);
tangent(two, twoD);
/* See if the slopes match, i.e.
dx1 / dy1 == dx2 / dy2
(dy1 * dy2) * dx1 / dy1 == (dy1 * dy2) * dx2 / dy2
dy2 * dx1 == dy1 * dx2
*/
int diff = UlpsDiff((float) (oneD.x * twoD.y), (float) (twoD.x * oneD.y));
if (diff < 0 || diff > ulps) {
return false;
}
/* See if the axis intercepts match, i.e.
y0 - x0 * dy / dx == y1 - x1 * dy / dx
dx * (y0 - x0 * dy / dx) == dx * (y1 - x1 * dy / dx)
dx * y0 - x0 * dy == dx * y1 - x1 * dy
*/
diff = UlpsDiff((float) (oneD.x * one[0].y - oneD.y * one[0].x),
(float) (oneD.x * two[0].y - oneD.y * two[0].x));
return diff >= 0 && diff <= ulps;
}
void tangent(const _Line& line, _Point& result) {
result.x = line[0].x - line[1].x;
result.y = line[0].y - line[1].y;
}

View File

@ -1,108 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
// Sources
// computer-aided design - volume 22 number 9 november 1990 pp 538 - 549
// online at http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
// This turns a line segment into a parameterized line, of the form
// ax + by + c = 0
// When a^2 + b^2 == 1, the line is normalized.
// The distance to the line for (x, y) is d(x,y) = ax + by + c
//
// Note that the distances below are not necessarily normalized. To get the true
// distance, it's necessary to either call normalize() after xxxEndPoints(), or
// divide the result of xxxDistance() by sqrt(normalSquared())
class LineParameters {
public:
void cubicEndPoints(const Cubic& pts) {
cubicEndPoints(pts, 0, 3);
}
void cubicEndPoints(const Cubic& pts, int s, int e) {
a = approximately_pin(pts[s].y - pts[e].y);
b = approximately_pin(pts[e].x - pts[s].x);
c = pts[s].x * pts[e].y - pts[e].x * pts[s].y;
}
void lineEndPoints(const _Line& pts) {
a = approximately_pin(pts[0].y - pts[1].y);
b = approximately_pin(pts[1].x - pts[0].x);
c = pts[0].x * pts[1].y - pts[1].x * pts[0].y;
}
void quadEndPoints(const Quadratic& pts) {
quadEndPoints(pts, 0, 2);
}
void quadEndPoints(const Quadratic& pts, int s, int e) {
a = approximately_pin(pts[s].y - pts[e].y);
b = approximately_pin(pts[e].x - pts[s].x);
c = pts[s].x * pts[e].y - pts[e].x * pts[s].y;
}
double normalSquared() const {
return a * a + b * b;
}
bool normalize() {
double normal = sqrt(normalSquared());
if (approximately_zero(normal)) {
a = b = c = 0;
return false;
}
double reciprocal = 1 / normal;
a *= reciprocal;
b *= reciprocal;
c *= reciprocal;
return true;
}
void cubicDistanceY(const Cubic& pts, Cubic& distance) const {
double oneThird = 1 / 3.0;
for (int index = 0; index < 4; ++index) {
distance[index].x = index * oneThird;
distance[index].y = a * pts[index].x + b * pts[index].y + c;
}
}
void quadDistanceY(const Quadratic& pts, Quadratic& distance) const {
double oneHalf = 1 / 2.0;
for (int index = 0; index < 3; ++index) {
distance[index].x = index * oneHalf;
distance[index].y = a * pts[index].x + b * pts[index].y + c;
}
}
double controlPtDistance(const Cubic& pts, int index) const {
SkASSERT(index == 1 || index == 2);
return a * pts[index].x + b * pts[index].y + c;
}
double controlPtDistance(const Quadratic& pts) const {
return a * pts[1].x + b * pts[1].y + c;
}
double pointDistance(const _Point& pt) const {
return a * pt.x + b * pt.y + c;
}
double dx() const {
return b;
}
double dy() const {
return -a;
}
private:
double a;
double b;
double c;
};

View File

@ -1,79 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Intersection_Tests.h"
#include "LineParameters.h"
// tests to verify that distance calculations are coded correctly
const Cubic tests[] = {
{{0, 0}, {1, 1}, {2, 2}, {0, 3}},
{{0, 0}, {1, 1}, {2, 2}, {3, 0}},
{{0, 0}, {5, 0}, {-2,4}, {3, 4}},
{{0, 2}, {1, 0}, {2, 0}, {3, 0}},
{{0, .2}, {1, 0}, {2, 0}, {3, 0}},
{{0, .02}, {1, 0}, {2, 0}, {3, 0}},
{{0, .002}, {1, 0}, {2, 0}, {3, 0}},
{{0, .0002}, {1, 0}, {2, 0}, {3, 0}},
{{0, .00002}, {1, 0}, {2, 0}, {3, 0}},
{{0, PointEpsilon * 2}, {1, 0}, {2, 0}, {3, 0}},
};
const double answers[][2] = {
{1, 2},
{1, 2},
{4, 4},
{1.1094003924, 0.5547001962},
{0.133038021, 0.06651901052},
{0.0133330370, 0.006666518523},
{0.001333333037, 0.0006666665185},
{0.000133333333, 6.666666652e-05},
{1.333333333e-05, 6.666666667e-06},
{1.333333333e-06, 6.666666667e-07},
};
const size_t tests_count = sizeof(tests) / sizeof(tests[0]);
static size_t firstLineParameterTest = 0;
void LineParameter_Test() {
for (size_t index = firstLineParameterTest; index < tests_count; ++index) {
LineParameters lineParameters;
const Cubic& cubic = tests[index];
lineParameters.cubicEndPoints(cubic);
double denormalizedDistance[2];
denormalizedDistance[0] = lineParameters.controlPtDistance(cubic, 1);
denormalizedDistance[1] = lineParameters.controlPtDistance(cubic, 2);
double normalSquared = lineParameters.normalSquared();
size_t inner;
for (inner = 0; inner < 2; ++inner) {
double distSq = denormalizedDistance[inner];
distSq *= distSq;
double answersSq = answers[index][inner];
answersSq *= answersSq;
if (AlmostEqualUlps(distSq, normalSquared * answersSq)) {
continue;
}
SkDebugf("%s [%d,%d] denormalizedDistance:%g != answer:%g"
" distSq:%g answerSq:%g normalSquared:%g\n",
__FUNCTION__, (int)index, (int)inner,
denormalizedDistance[inner], answers[index][inner],
distSq, answersSq, normalSquared);
}
lineParameters.normalize();
double normalizedDistance[2];
normalizedDistance[0] = lineParameters.controlPtDistance(cubic, 1);
normalizedDistance[1] = lineParameters.controlPtDistance(cubic, 2);
for (inner = 0; inner < 2; ++inner) {
if (AlmostEqualUlps(fabs(normalizedDistance[inner]), answers[index][inner])) {
continue;
}
SkDebugf("%s [%d,%d] normalizedDistance:%1.10g != answer:%g\n",
__FUNCTION__, (int)index, (int)inner,
normalizedDistance[inner], answers[index][inner]);
}
}
}

View File

@ -1,369 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Intersections.h"
#include "LineUtilities.h"
#include "QuadraticUtilities.h"
/*
Find the interection of a line and quadratic by solving for valid t values.
From http://stackoverflow.com/questions/1853637/how-to-find-the-mathematical-function-defining-a-bezier-curve
"A Bezier curve is a parametric function. A quadratic Bezier curve (i.e. three
control points) can be expressed as: F(t) = A(1 - t)^2 + B(1 - t)t + Ct^2 where
A, B and C are points and t goes from zero to one.
This will give you two equations:
x = a(1 - t)^2 + b(1 - t)t + ct^2
y = d(1 - t)^2 + e(1 - t)t + ft^2
If you add for instance the line equation (y = kx + m) to that, you'll end up
with three equations and three unknowns (x, y and t)."
Similar to above, the quadratic is represented as
x = a(1-t)^2 + 2b(1-t)t + ct^2
y = d(1-t)^2 + 2e(1-t)t + ft^2
and the line as
y = g*x + h
Using Mathematica, solve for the values of t where the quadratic intersects the
line:
(in) t1 = Resultant[a*(1 - t)^2 + 2*b*(1 - t)*t + c*t^2 - x,
d*(1 - t)^2 + 2*e*(1 - t)*t + f*t^2 - g*x - h, x]
(out) -d + h + 2 d t - 2 e t - d t^2 + 2 e t^2 - f t^2 +
g (a - 2 a t + 2 b t + a t^2 - 2 b t^2 + c t^2)
(in) Solve[t1 == 0, t]
(out) {
{t -> (-2 d + 2 e + 2 a g - 2 b g -
Sqrt[(2 d - 2 e - 2 a g + 2 b g)^2 -
4 (-d + 2 e - f + a g - 2 b g + c g) (-d + a g + h)]) /
(2 (-d + 2 e - f + a g - 2 b g + c g))
},
{t -> (-2 d + 2 e + 2 a g - 2 b g +
Sqrt[(2 d - 2 e - 2 a g + 2 b g)^2 -
4 (-d + 2 e - f + a g - 2 b g + c g) (-d + a g + h)]) /
(2 (-d + 2 e - f + a g - 2 b g + c g))
}
}
Using the results above (when the line tends towards horizontal)
A = (-(d - 2*e + f) + g*(a - 2*b + c) )
B = 2*( (d - e ) - g*(a - b ) )
C = (-(d ) + g*(a ) + h )
If g goes to infinity, we can rewrite the line in terms of x.
x = g'*y + h'
And solve accordingly in Mathematica:
(in) t2 = Resultant[a*(1 - t)^2 + 2*b*(1 - t)*t + c*t^2 - g'*y - h',
d*(1 - t)^2 + 2*e*(1 - t)*t + f*t^2 - y, y]
(out) a - h' - 2 a t + 2 b t + a t^2 - 2 b t^2 + c t^2 -
g' (d - 2 d t + 2 e t + d t^2 - 2 e t^2 + f t^2)
(in) Solve[t2 == 0, t]
(out) {
{t -> (2 a - 2 b - 2 d g' + 2 e g' -
Sqrt[(-2 a + 2 b + 2 d g' - 2 e g')^2 -
4 (a - 2 b + c - d g' + 2 e g' - f g') (a - d g' - h')]) /
(2 (a - 2 b + c - d g' + 2 e g' - f g'))
},
{t -> (2 a - 2 b - 2 d g' + 2 e g' +
Sqrt[(-2 a + 2 b + 2 d g' - 2 e g')^2 -
4 (a - 2 b + c - d g' + 2 e g' - f g') (a - d g' - h')])/
(2 (a - 2 b + c - d g' + 2 e g' - f g'))
}
}
Thus, if the slope of the line tends towards vertical, we use:
A = ( (a - 2*b + c) - g'*(d - 2*e + f) )
B = 2*(-(a - b ) + g'*(d - e ) )
C = ( (a ) - g'*(d ) - h' )
*/
class LineQuadraticIntersections {
public:
LineQuadraticIntersections(const Quadratic& q, const _Line& l, Intersections& i)
: quad(q)
, line(l)
, intersections(i) {
}
int intersectRay(double roots[2]) {
/*
solve by rotating line+quad so line is horizontal, then finding the roots
set up matrix to rotate quad to x-axis
|cos(a) -sin(a)|
|sin(a) cos(a)|
note that cos(a) = A(djacent) / Hypoteneuse
sin(a) = O(pposite) / Hypoteneuse
since we are computing Ts, we can ignore hypoteneuse, the scale factor:
| A -O |
| O A |
A = line[1].x - line[0].x (adjacent side of the right triangle)
O = line[1].y - line[0].y (opposite side of the right triangle)
for each of the three points (e.g. n = 0 to 2)
quad[n].y' = (quad[n].y - line[0].y) * A - (quad[n].x - line[0].x) * O
*/
double adj = line[1].x - line[0].x;
double opp = line[1].y - line[0].y;
double r[3];
for (int n = 0; n < 3; ++n) {
r[n] = (quad[n].y - line[0].y) * adj - (quad[n].x - line[0].x) * opp;
}
double A = r[2];
double B = r[1];
double C = r[0];
A += C - 2 * B; // A = a - 2*b + c
B -= C; // B = -(b - c)
return quadraticRootsValidT(A, 2 * B, C, roots);
}
int intersect() {
addEndPoints();
double rootVals[2];
int roots = intersectRay(rootVals);
for (int index = 0; index < roots; ++index) {
double quadT = rootVals[index];
double lineT = findLineT(quadT);
if (pinTs(quadT, lineT)) {
_Point pt;
xy_at_t(line, lineT, pt.x, pt.y);
intersections.insert(quadT, lineT, pt);
}
}
return intersections.fUsed;
}
int horizontalIntersect(double axisIntercept, double roots[2]) {
double D = quad[2].y; // f
double E = quad[1].y; // e
double F = quad[0].y; // d
D += F - 2 * E; // D = d - 2*e + f
E -= F; // E = -(d - e)
F -= axisIntercept;
return quadraticRootsValidT(D, 2 * E, F, roots);
}
int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) {
addHorizontalEndPoints(left, right, axisIntercept);
double rootVals[2];
int roots = horizontalIntersect(axisIntercept, rootVals);
for (int index = 0; index < roots; ++index) {
_Point pt;
double quadT = rootVals[index];
xy_at_t(quad, quadT, pt.x, pt.y);
double lineT = (pt.x - left) / (right - left);
if (pinTs(quadT, lineT)) {
intersections.insert(quadT, lineT, pt);
}
}
if (flipped) {
flip();
}
return intersections.fUsed;
}
int verticalIntersect(double axisIntercept, double roots[2]) {
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 quadraticRootsValidT(D, 2 * E, F, roots);
}
int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) {
addVerticalEndPoints(top, bottom, axisIntercept);
double rootVals[2];
int roots = verticalIntersect(axisIntercept, rootVals);
for (int index = 0; index < roots; ++index) {
_Point pt;
double quadT = rootVals[index];
xy_at_t(quad, quadT, pt.x, pt.y);
double lineT = (pt.y - top) / (bottom - top);
if (pinTs(quadT, lineT)) {
intersections.insert(quadT, lineT, pt);
}
}
if (flipped) {
flip();
}
return intersections.fUsed;
}
protected:
// add endpoints first to get zero and one t values exactly
void addEndPoints()
{
for (int qIndex = 0; qIndex < 3; qIndex += 2) {
for (int lIndex = 0; lIndex < 2; lIndex++) {
if (quad[qIndex] == line[lIndex]) {
intersections.insert(qIndex >> 1, lIndex, line[lIndex]);
}
}
}
}
void addHorizontalEndPoints(double left, double right, double y)
{
for (int qIndex = 0; qIndex < 3; qIndex += 2) {
if (quad[qIndex].y != y) {
continue;
}
if (quad[qIndex].x == left) {
intersections.insert(qIndex >> 1, 0, quad[qIndex]);
}
if (quad[qIndex].x == right) {
intersections.insert(qIndex >> 1, 1, quad[qIndex]);
}
}
}
void addVerticalEndPoints(double top, double bottom, double x)
{
for (int qIndex = 0; qIndex < 3; qIndex += 2) {
if (quad[qIndex].x != x) {
continue;
}
if (quad[qIndex].y == top) {
intersections.insert(qIndex >> 1, 0, quad[qIndex]);
}
if (quad[qIndex].y == bottom) {
intersections.insert(qIndex >> 1, 1, quad[qIndex]);
}
}
}
double findLineT(double t) {
double x, y;
xy_at_t(quad, t, x, y);
double dx = line[1].x - line[0].x;
double dy = line[1].y - line[0].y;
if (fabs(dx) > fabs(dy)) {
return (x - line[0].x) / dx;
}
return (y - line[0].y) / dy;
}
void flip() {
// OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
int roots = intersections.fUsed;
for (int index = 0; index < roots; ++index) {
intersections.fT[1][index] = 1 - intersections.fT[1][index];
}
}
static bool pinTs(double& quadT, double& lineT) {
if (!approximately_one_or_less(lineT)) {
return false;
}
if (!approximately_zero_or_more(lineT)) {
return false;
}
if (precisely_less_than_zero(quadT)) {
quadT = 0;
} else if (precisely_greater_than_one(quadT)) {
quadT = 1;
}
if (precisely_less_than_zero(lineT)) {
lineT = 0;
} else if (precisely_greater_than_one(lineT)) {
lineT = 1;
}
return true;
}
private:
const Quadratic& quad;
const _Line& line;
Intersections& intersections;
};
// utility for pairs of coincident quads
static double horizontalIntersect(const Quadratic& quad, const _Point& pt) {
LineQuadraticIntersections q(quad, *((_Line*) 0), *((Intersections*) 0));
double rootVals[2];
int roots = q.horizontalIntersect(pt.y, rootVals);
for (int index = 0; index < roots; ++index) {
double x;
double t = rootVals[index];
xy_at_t(quad, t, x, *(double*) 0);
if (AlmostEqualUlps(x, pt.x)) {
return t;
}
}
return -1;
}
static double verticalIntersect(const Quadratic& quad, const _Point& pt) {
LineQuadraticIntersections q(quad, *((_Line*) 0), *((Intersections*) 0));
double rootVals[2];
int roots = q.verticalIntersect(pt.x, rootVals);
for (int index = 0; index < roots; ++index) {
double y;
double t = rootVals[index];
xy_at_t(quad, t, *(double*) 0, y);
if (AlmostEqualUlps(y, pt.y)) {
return t;
}
}
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]) {
LineQuadraticIntersections q(quad, *((_Line*) 0), *((Intersections*) 0));
double rootVals[2];
int result = q.horizontalIntersect(y, rootVals);
int tCount = 0;
for (int index = 0; index < result; ++index) {
double x, y;
xy_at_t(quad, rootVals[index], x, y);
if (x < left || x > right) {
continue;
}
tRange[tCount++] = rootVals[index];
}
return tCount;
}
int horizontalIntersect(const Quadratic& quad, double left, double right, double y,
bool flipped, Intersections& intersections) {
LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
return q.horizontalIntersect(y, left, right, flipped);
}
int verticalIntersect(const Quadratic& quad, double top, double bottom, double x,
bool flipped, Intersections& intersections) {
LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
return q.verticalIntersect(x, top, bottom, flipped);
}
int intersect(const Quadratic& quad, const _Line& line, Intersections& i) {
LineQuadraticIntersections q(quad, line, i);
return q.intersect();
}
int intersectRay(const Quadratic& quad, const _Line& line, Intersections& i) {
LineQuadraticIntersections q(quad, line, i);
return q.intersectRay(i.fT[0]);
}

View File

@ -1,235 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "Intersections.h"
#include "TestUtilities.h"
struct lineQuad {
Quadratic quad;
_Line line;
int result;
_Point expected[2];
} lineQuadTests[] = {
// quad line results
{{{1, 1}, {2, 1}, {0, 2}}, {{0, 0}, {1, 1}}, 1, {{1, 1} }},
{{{0, 0}, {1, 1}, {3, 1}}, {{0, 0}, {3, 1}}, 2, {{0, 0}, {3, 1}}},
{{{2, 0}, {1, 1}, {2, 2}}, {{0, 0}, {0, 2}}, 0 },
{{{4, 0}, {0, 1}, {4, 2}}, {{3, 1}, {4, 1}}, 0, },
{{{0, 0}, {0, 1}, {1, 1}}, {{0, 1}, {1, 0}}, 1, {{.25, .75} }},
};
size_t lineQuadTests_count = sizeof(lineQuadTests) / sizeof(lineQuadTests[0]);
const int firstLineQuadIntersectionTest = 0;
static int doIntersect(Intersections& intersections, const Quadratic& quad, const _Line& line, bool& flipped) {
int result;
flipped = false;
if (line[0].x == line[1].x) {
double top = line[0].y;
double bottom = line[1].y;
flipped = top > bottom;
if (flipped) {
SkTSwap<double>(top, bottom);
}
result = verticalIntersect(quad, top, bottom, line[0].x, flipped, intersections);
} else if (line[0].y == line[1].y) {
double left = line[0].x;
double right = line[1].x;
flipped = left > right;
if (flipped) {
SkTSwap<double>(left, right);
}
result = horizontalIntersect(quad, left, right, line[0].y, flipped, intersections);
} else {
intersect(quad, line, intersections);
result = intersections.fUsed;
}
return result;
}
static struct oneLineQuad {
Quadratic quad;
_Line line;
} oneOffs[] = {
{{{369.848602,145.680267}, {382.360413,121.298294}, {406.207703,121.298294}},
{{406.207703,121.298294}, {348.781738,123.864815}}}
};
static size_t oneOffs_count = sizeof(oneOffs) / sizeof(oneOffs[0]);
static void testOneOffs() {
Intersections intersections;
bool flipped = false;
for (size_t index = 0; index < oneOffs_count; ++index) {
const Quadratic& quad = oneOffs[index].quad;
const _Line& line = oneOffs[index].line;
int result = doIntersect(intersections, quad, line, flipped);
for (int inner = 0; inner < result; ++inner) {
double quadT = intersections.fT[0][inner];
double quadX, quadY;
xy_at_t(quad, quadT, quadX, quadY);
double lineT = intersections.fT[1][inner];
double lineX, lineY;
xy_at_t(line, lineT, lineX, lineY);
SkASSERT(AlmostEqualUlps(quadX, lineX)
&& AlmostEqualUlps(quadY, lineY));
}
}
}
void LineQuadraticIntersection_Test() {
if (1) {
testOneOffs();
}
for (size_t index = firstLineQuadIntersectionTest; index < lineQuadTests_count; ++index) {
const Quadratic& quad = lineQuadTests[index].quad;
const _Line& line = lineQuadTests[index].line;
Quadratic reduce1;
_Line reduce2;
int order1 = reduceOrder(quad, reduce1, kReduceOrder_TreatAsFill);
int order2 = reduceOrder(line, reduce2);
if (order1 < 3) {
SkDebugf("%s [%d] quad order=%d\n", __FUNCTION__, (int) index, order1);
SkASSERT(0);
}
if (order2 < 2) {
SkDebugf("%s [%d] line order=%d\n", __FUNCTION__, (int) index, order2);
SkASSERT(0);
}
Intersections intersections;
bool flipped = false;
int result = doIntersect(intersections, quad, line, flipped);
SkASSERT(result == lineQuadTests[index].result);
if (!intersections.intersected()) {
continue;
}
for (int pt = 0; pt < result; ++pt) {
double tt1 = intersections.fT[0][pt];
SkASSERT(tt1 >= 0 && tt1 <= 1);
_Point t1, t2;
xy_at_t(quad, tt1, t1.x, t1.y);
double tt2 = intersections.fT[1][pt];
SkASSERT(tt2 >= 0 && tt2 <= 1);
xy_at_t(line, tt2, t2.x, t2.y);
if (!AlmostEqualUlps(t1.x, t2.x)) {
SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n",
__FUNCTION__, (int)index, pt, tt1, t1.x, t1.y, tt2, t2.x, t2.y);
SkASSERT(0);
}
if (!AlmostEqualUlps(t1.y, t2.y)) {
SkDebugf("%s [%d,%d] y!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n",
__FUNCTION__, (int)index, pt, tt1, t1.x, t1.y, tt2, t2.x, t2.y);
SkASSERT(0);
}
if (!t1.approximatelyEqual(lineQuadTests[index].expected[0])
&& (lineQuadTests[index].result == 1
|| !t1.approximatelyEqual(lineQuadTests[index].expected[1]))) {
SkDebugf("%s t1=(%1.9g,%1.9g)\n", __FUNCTION__, t1.x, t1.y);
SkASSERT(0);
}
}
}
}
static void testLineIntersect(State4& state, const Quadratic& quad, const _Line& line,
const double x, const double y) {
char pathStr[1024];
bzero(pathStr, sizeof(pathStr));
char* str = pathStr;
str += sprintf(str, " path.moveTo(%1.9g, %1.9g);\n", quad[0].x, quad[0].y);
str += sprintf(str, " path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n", quad[1].x, quad[1].y, quad[2].x, quad[2].y);
str += sprintf(str, " path.moveTo(%1.9g, %1.9g);\n", line[0].x, line[0].y);
str += sprintf(str, " path.lineTo(%1.9g, %1.9g);\n", line[1].x, line[1].y);
Intersections intersections;
bool flipped = false;
int result = doIntersect(intersections, quad, line, flipped);
bool found = false;
for (int index = 0; index < result; ++index) {
double quadT = intersections.fT[0][index];
double quadX, quadY;
xy_at_t(quad, quadT, quadX, quadY);
double lineT = intersections.fT[1][index];
double lineX, lineY;
xy_at_t(line, lineT, lineX, lineY);
if (fabs(quadX - lineX) < FLT_EPSILON && fabs(quadY - lineY) < FLT_EPSILON
&& fabs(x - lineX) < FLT_EPSILON && fabs(y - lineY) < FLT_EPSILON) {
found = true;
}
}
SkASSERT(found);
state.testsRun++;
}
// find a point on a quad by choosing a t from 0 to 1
// create a vertical span above and below the point
// verify that intersecting the vertical span and the quad returns t
// verify that a vertical span starting at quad[0] intersects at t=0
// verify that a vertical span starting at quad[2] intersects at t=1
static void* testQuadLineIntersectMain(void* data)
{
SkASSERT(data);
State4& state = *(State4*) data;
do {
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;
Quadratic quad = {{ax, ay}, {bx, by}, {cx, cy}};
Quadratic reduced;
int order = reduceOrder(quad, reduced, kReduceOrder_TreatAsFill);
if (order < 3) {
continue; // skip degenerates
}
for (int tIndex = 0; tIndex <= 4; ++tIndex) {
double x, y;
xy_at_t(quad, tIndex / 4.0, x, y);
for (int h = -2; h <= 2; ++h) {
for (int v = -2; v <= 2; ++v) {
if (h == v && abs(h) != 1) {
continue;
}
_Line line = {{x - h, y - v}, {x, y}};
testLineIntersect(state, quad, line, x, y);
_Line line2 = {{x, y}, {x + h, y + v}};
testLineIntersect(state, quad, line2, x, y);
_Line line3 = {{x - h, y - v}, {x + h, y + v}};
testLineIntersect(state, quad, line3, x, y);
}
}
}
} while (runNextTestSet(state));
return NULL;
}
void QuadLineIntersectThreaded_Test(int& testsRun)
{
SkDebugf("%s\n", __FUNCTION__);
const char testStr[] = "testQuadLineIntersect";
initializeTests(testStr, sizeof(testStr));
int testsStart = testsRun;
for (int a = 0; a < 16; ++a) {
for (int b = 0 ; b < 16; ++b) {
for (int c = 0 ; c < 16; ++c) {
testsRun += dispatchTest4(testQuadLineIntersectMain,
a, b, c, 0);
}
if (!gRunTestsInOneThread) SkDebugf(".");
}
if (!gRunTestsInOneThread) SkDebugf("%d", a);
}
testsRun += waitForCompletion();
SkDebugf("\n%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
}

View File

@ -1,133 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "LineUtilities.h"
bool implicitLine(const _Line& line, double& slope, double& axisIntercept) {
_Point delta;
tangent(line, delta);
bool moreHorizontal = fabs(delta.x) > fabs(delta.y);
if (moreHorizontal) {
slope = delta.y / delta.x;
axisIntercept = line[0].y - slope * line[0].x;
} else {
slope = delta.x / delta.y;
axisIntercept = line[0].x - slope * line[0].y;
}
return moreHorizontal;
}
int reduceOrder(const _Line& line, _Line& reduced) {
reduced[0] = line[0];
int different = line[0] != line[1];
reduced[1] = line[different];
return 1 + different;
}
void sub_divide(const _Line& line, double t1, double t2, _Line& dst) {
_Point delta;
tangent(line, delta);
dst[0].x = line[0].x - t1 * delta.x;
dst[0].y = line[0].y - t1 * delta.y;
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
// return (float) ((P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y));
double is_left(const _Line& line, const _Point& pt) {
_Vector P0 = line[1] - line[0];
_Vector P2 = pt - line[0];
return P0.cross(P2);
}
double t_at(const _Line& line, const _Point& pt) {
double dx = line[1].x - line[0].x;
double dy = line[1].y - line[0].y;
if (fabs(dx) > fabs(dy)) {
if (approximately_zero(dx)) {
return 0;
}
return (pt.x - line[0].x) / dx;
}
if (approximately_zero(dy)) {
return 0;
}
return (pt.y - line[0].y) / dy;
}
static void setMinMax(double x, int flags, double& minX, double& maxX) {
if (minX > x && (flags & (kFindTopMin | kFindBottomMin))) {
minX = x;
}
if (maxX < x && (flags & (kFindTopMax | kFindBottomMax))) {
maxX = x;
}
}
void x_at(const _Point& p1, const _Point& p2, double top, double bottom,
int flags, double& minX, double& maxX) {
if (AlmostEqualUlps(p1.y, p2.y)) {
// It should be OK to bail early in this case. There's another edge
// which shares this end point which can intersect without failing to
// have a slope ... maybe
return;
}
// p2.x is always greater than p1.x -- the part of points (p1, p2) are
// moving from the start of the cubic towards its end.
// if p1.y < p2.y, minX can be affected
// if p1.y > p2.y, maxX can be affected
double slope = (p2.x - p1.x) / (p2.y - p1.y);
int topFlags = flags & (kFindTopMin | kFindTopMax);
if (topFlags && ((top <= p1.y && top >= p2.y)
|| (top >= p1.y && top <= p2.y))) {
double x = p1.x + (top - p1.y) * slope;
setMinMax(x, topFlags, minX, maxX);
}
int bottomFlags = flags & (kFindBottomMin | kFindBottomMax);
if (bottomFlags && ((bottom <= p1.y && bottom >= p2.y)
|| (bottom >= p1.y && bottom <= p2.y))) {
double x = p1.x + (bottom - p1.y) * slope;
setMinMax(x, bottomFlags, minX, maxX);
}
}
void xy_at_t(const _Line& line, double t, double& x, double& y) {
double one_t = 1 - t;
if (&x) {
x = one_t * line[0].x + t * line[1].x;
}
if (&y) {
y = one_t * line[0].y + t * line[1].y;
}
}
_Point xy_at_t(const _Line& line, double t) {
double one_t = 1 - t;
_Point result = { one_t * line[0].x + t * line[1].x, one_t * line[0].y + t * line[1].y };
return result;
}

View File

@ -1,25 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
bool implicitLine(const _Line& line, double& slope, double& axisIntercept);
int reduceOrder(const _Line& line, _Line& reduced);
double is_left(const _Line& line, const _Point& pt);
void sub_divide(const _Line& src, double t1, double t2, _Line& dst);
double t_at(const _Line&, const _Point& );
void xy_at_t(const _Line& , double t, double& x, double& y);
_Point xy_at_t(const _Line& , double t);
enum x_at_flags {
kFindTopMin = 1,
kFindTopMax = 2,
kFindBottomMin = 4,
kFindBottomMax = 8
};
void x_at(const _Point& p1, const _Point& p2, double minY, double maxY,
int flags, double& tMin, double& tMax);

View File

@ -1,353 +0,0 @@
#include <ctype.h>
#include "SkPath.h"
#include "SkParse.h"
#include "SkPoint.h"
#include "SkUtils.h"
#define QUADRATIC_APPROXIMATION 0
const char logoStr[] =
"<path fill=\"#0081C6\""
"d=\"M440.51,289.479c1.623,1.342,5.01,4.164,5.01,9.531c0,5.223-2.965,7.697-5.93,10.024"
"c-0.918,0.916-1.977,1.907-1.977,3.462c0,1.551,1.059,2.397,1.834,3.035l2.545,1.973c3.105,2.613,5.928,5.016,5.928,9.889"
"c0,6.635-6.426,13.341-18.566,13.341c-10.238,0-15.178-4.87-15.178-10.097c0-2.543,1.268-6.139,5.438-8.613"
"c4.373-2.682,10.307-3.033,13.482-3.249c-0.99-1.271-2.119-2.61-2.119-4.798c0-1.199,0.355-1.907,0.707-2.754"
"c-0.779,0.07-1.553,0.141-2.26,0.141c-7.482,0-11.719-5.579-11.719-11.082c0-3.247,1.484-6.851,4.518-9.461"
"c4.025-3.318,8.824-3.883,12.639-3.883h14.541l-4.518,2.541H440.51z"
"M435.494,320.826c-0.562-0.072-0.916-0.072-1.619-0.072"
"c-0.637,0-4.451,0.143-7.416,1.132c-1.553,0.564-6.07,2.257-6.07,7.271c0,5.013,4.873,8.615,12.426,8.615"
"c6.775,0,10.379-3.253,10.379-7.624C443.193,326.54,440.863,324.64,435.494,320.826z"
"M437.543,307.412"
"c1.623-1.627,1.764-3.883,1.764-5.154c0-5.083-3.035-12.99-8.893-12.99c-1.838,0-3.812,0.918-4.945,2.331"
"c-1.199,1.483-1.551,3.387-1.551,5.225c0,4.729,2.754,12.565,8.826,12.565C434.508,309.389,436.41,308.543,437.543,307.412z\"/>"
"<path fill=\"#FFD200\""
"d=\"M396.064,319.696c-11.206,0-17.198-8.739-17.198-16.636c0-9.233,7.542-17.126,18.258-17.126"
"c10.357,0,16.844,8.104,16.844,16.635C413.969,310.884,407.557,319.696,396.064,319.696z"
"M404.873,313.987"
"c1.695-2.257,2.119-5.074,2.119-7.826c0-6.202-2.961-18.042-11.701-18.042c-2.326,0-4.652,0.918-6.342,2.399"
"c-2.749,2.465-3.245,5.566-3.245,8.599c0,6.977,3.454,18.463,11.984,18.463C400.436,317.58,403.256,316.242,404.873,313.987z\"/>"
"<path fill=\"#ED174F\""
"d=\"M357.861,319.696c-11.207,0-17.199-8.739-17.199-16.636c0-9.233,7.544-17.126,18.258-17.126"
"c10.359,0,16.845,8.104,16.845,16.635C375.764,310.884,369.351,319.696,357.861,319.696z"
"M366.671,313.987"
"c1.693-2.257,2.116-5.074,2.116-7.826c0-6.202-2.961-18.042-11.701-18.042c-2.325,0-4.652,0.918-6.344,2.399"
"c-2.749,2.465-3.241,5.566-3.241,8.599c0,6.977,3.452,18.463,11.983,18.463C362.234,317.58,365.053,316.242,366.671,313.987z\"/>"
"<path fill=\"#0081C6\""
"d=\"M335.278,318.591l-10.135,2.339c-4.111,0.638-7.795,1.204-11.69,1.204"
"c-19.56,0-26.998-14.386-26.998-25.654c0-13.746,10.558-26.498,28.629-26.498c3.827,0,7.51,0.564,10.839,1.486"
"c5.316,1.488,7.796,3.331,9.355,4.394l-5.883,5.599l-2.479,0.565l1.771-2.837c-2.408-2.336-6.805-6.658-15.164-6.658"
"c-11.196,0-19.63,8.507-19.63,20.906c0,13.319,9.638,25.861,25.084,25.861c4.539,0,6.874-0.918,9-1.771v-11.407l-10.698,0.566"
"l5.667-3.047h15.023l-1.841,1.77c-0.5,0.424-0.567,0.57-0.71,1.133c-0.073,0.64-0.141,2.695-0.141,3.403V318.591z\"/>"
"<path fill=\"#49A942\""
"d=\"M462.908,316.552c-2.342-0.214-2.832-0.638-2.832-3.401v-0.782v-39.327c0.014-0.153,0.025-0.31,0.041-0.457"
"c0.283-2.479,0.992-2.903,3.189-4.182h-10.135l-5.316,2.552h5.418v0.032l-0.004-0.024v41.406v2.341"
"c0,1.416-0.281,1.629-1.912,3.753H463.9l2.623-1.557C465.318,316.763,464.113,316.692,462.908,316.552z\"/>"
"<path fill=\"#ED174F\""
"d=\"M491.742,317.203c-0.771,0.422-1.547,0.916-2.318,1.268c-2.326,1.055-4.719,1.336-6.83,1.336"
"c-2.25,0-5.77-0.143-9.361-2.744c-4.992-3.521-7.176-9.572-7.176-14.851c0-10.906,8.869-16.255,16.115-16.255"
"c2.533,0,5.141,0.633,7.252,1.972c3.516,2.318,4.43,5.344,4.922,6.963l-16.535,6.688l-5.422,0.422"
"c1.758,8.938,7.812,14.145,14.498,14.145c3.59,0,6.193-1.266,8.586-2.461L491.742,317.203z"
"M485.129,296.229"
"c1.336-0.493,2.039-0.914,2.039-1.899c0-2.812-3.166-6.053-6.967-6.053c-2.818,0-8.094,2.183-8.094,9.783"
"c0,1.197,0.141,2.464,0.213,3.73L485.129,296.229z\"/>"
"<path fill=\"#77787B\""
"d=\"M498.535,286.439v4.643h-0.564v-4.643h-1.537v-0.482h3.637v0.482H498.535z\"/>"
"<path fill=\"#77787B\""
"d=\"M504.863,291.082v-4.687h-0.023l-1.432,4.687h-0.439l-1.443-4.687h-0.02v4.687h-0.512v-5.125h0.877"
"l1.307,4.143h0.018l1.285-4.143h0.891v5.125H504.863z\"/>"
;
size_t logoStrLen = sizeof(logoStr);
#if QUADRATIC_APPROXIMATION
////////////////////////////////////////////////////////////////////////////////////
//functions to approximate a cubic using two quadratics
// midPt sets the first argument to be the midpoint of the other two
// it is used by quadApprox
static inline void midPt(SkPoint& dest,const SkPoint& a,const SkPoint& b)
{
dest.set(SkScalarAve(a.fX, b.fX),SkScalarAve(a.fY, b.fY));
}
// quadApprox - makes an approximation, which we hope is faster
static void quadApprox(SkPath &fPath, const SkPoint &p0, const SkPoint &p1, const SkPoint &p2)
{
//divide the cubic up into two cubics, then convert them into quadratics
//define our points
SkPoint c,j,k,l,m,n,o,p,q, mid;
fPath.getLastPt(&c);
midPt(j, p0, c);
midPt(k, p0, p1);
midPt(l, p1, p2);
midPt(o, j, k);
midPt(p, k, l);
midPt(q, o, p);
//compute the first half
m.set(SkScalarHalf(3*j.fX - c.fX), SkScalarHalf(3*j.fY - c.fY));
n.set(SkScalarHalf(3*o.fX -q.fX), SkScalarHalf(3*o.fY - q.fY));
midPt(mid,m,n);
fPath.quadTo(mid,q);
c = q;
//compute the second half
m.set(SkScalarHalf(3*p.fX - c.fX), SkScalarHalf(3*p.fY - c.fY));
n.set(SkScalarHalf(3*l.fX -p2.fX),SkScalarHalf(3*l.fY -p2.fY));
midPt(mid,m,n);
fPath.quadTo(mid,p2);
}
#endif
static inline bool is_between(int c, int min, int max)
{
return (unsigned)(c - min) <= (unsigned)(max - min);
}
static inline bool is_ws(int c)
{
return is_between(c, 1, 32);
}
static inline bool is_digit(int c)
{
return is_between(c, '0', '9');
}
static inline bool is_sep(int c)
{
return is_ws(c) || c == ',';
}
static const char* skip_ws(const char str[])
{
SkASSERT(str);
while (is_ws(*str))
str++;
return str;
}
static const char* skip_sep(const char str[])
{
SkASSERT(str);
while (is_sep(*str))
str++;
return str;
}
static const char* find_points(const char str[], SkPoint value[], int count,
bool isRelative, SkPoint* relative)
{
str = SkParse::FindScalars(str, &value[0].fX, count * 2);
if (isRelative) {
for (int index = 0; index < count; index++) {
value[index].fX += relative->fX;
value[index].fY += relative->fY;
}
}
return str;
}
static const char* find_scalar(const char str[], SkScalar* value,
bool isRelative, SkScalar relative)
{
str = SkParse::FindScalar(str, value);
if (isRelative)
*value += relative;
return str;
}
static void showPathContour(SkPath::Iter& iter) {
uint8_t verb;
SkPoint pts[4];
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
SkDebugf("path.moveTo(%1.9gf,%1.9gf);\n", pts[0].fX, pts[0].fY);
continue;
case SkPath::kLine_Verb:
SkDebugf("path.lineTo(%1.9gf,%1.9gf);\n", pts[1].fX, pts[1].fY);
break;
case SkPath::kQuad_Verb:
SkDebugf("path.quadTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf);\n",
pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
break;
case SkPath::kCubic_Verb:
SkDebugf("path.cubicTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf, %1.9gf,%1.9gf);\n",
pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
break;
case SkPath::kClose_Verb:
SkDebugf("path.close();\n");
break;
default:
SkDEBUGFAIL("bad verb");
return;
}
}
}
static void showPath(const SkPath& path) {
SkPath::Iter iter(path, true);
int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
if (rectCount > 0) {
SkTDArray<SkRect> rects;
SkTDArray<SkPath::Direction> directions;
rects.setCount(rectCount);
directions.setCount(rectCount);
path.rectContours(rects.begin(), directions.begin());
for (int contour = 0; contour < rectCount; ++contour) {
const SkRect& rect = rects[contour];
SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
}
return;
}
iter.setPath(path, true);
showPathContour(iter);
}
static const char* parsePath(const char* data) {
SkPath fPath;
SkPoint f = {0, 0};
SkPoint c = {0, 0};
SkPoint lastc = {0, 0};
SkPoint points[3];
char op = '\0';
char previousOp = '\0';
bool relative = false;
do {
data = skip_ws(data);
if (data[0] == '\0')
break;
char ch = data[0];
if (is_digit(ch) || ch == '-' || ch == '+') {
if (op == '\0') {
SkASSERT(0);
return 0;
}
}
else {
op = ch;
relative = false;
if (islower(op)) {
op = (char) toupper(op);
relative = true;
}
data++;
data = skip_sep(data);
}
switch (op) {
case 'M':
data = find_points(data, points, 1, relative, &c);
fPath.moveTo(points[0]);
op = 'L';
c = points[0];
break;
case 'L':
data = find_points(data, points, 1, relative, &c);
fPath.lineTo(points[0]);
c = points[0];
break;
case 'H': {
SkScalar x;
data = find_scalar(data, &x, relative, c.fX);
fPath.lineTo(x, c.fY);
c.fX = x;
}
break;
case 'V': {
SkScalar y;
data = find_scalar(data, &y, relative, c.fY);
fPath.lineTo(c.fX, y);
c.fY = y;
}
break;
case 'C':
data = find_points(data, points, 3, relative, &c);
goto cubicCommon;
case 'S':
data = find_points(data, &points[1], 2, relative, &c);
points[0] = c;
if (previousOp == 'C' || previousOp == 'S') {
points[0].fX -= lastc.fX - c.fX;
points[0].fY -= lastc.fY - c.fY;
}
cubicCommon:
// if (data[0] == '\0')
// return;
#if QUADRATIC_APPROXIMATION
quadApprox(fPath, points[0], points[1], points[2]);
#else //this way just does a boring, slow old cubic
fPath.cubicTo(points[0], points[1], points[2]);
#endif
//if we are using the quadApprox, lastc is what it would have been if we had used
//cubicTo
lastc = points[1];
c = points[2];
break;
case 'Q': // Quadratic Bezier Curve
data = find_points(data, points, 2, relative, &c);
goto quadraticCommon;
case 'T':
data = find_points(data, &points[1], 1, relative, &c);
points[0] = points[1];
if (previousOp == 'Q' || previousOp == 'T') {
points[0].fX = c.fX * 2 - lastc.fX;
points[0].fY = c.fY * 2 - lastc.fY;
}
quadraticCommon:
fPath.quadTo(points[0], points[1]);
lastc = points[0];
c = points[1];
break;
case 'Z':
fPath.close();
#if 0 // !!! still a bug?
if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) {
c.fX -= SkScalar.Epsilon; // !!! enough?
fPath.moveTo(c);
fPath.lineTo(f);
fPath.close();
}
#endif
c = f;
op = '\0';
break;
case '~': {
SkPoint args[2];
data = find_points(data, args, 2, false, NULL);
fPath.moveTo(args[0].fX, args[0].fY);
fPath.lineTo(args[1].fX, args[1].fY);
}
break;
default:
SkASSERT(0);
return 0;
}
if (previousOp == 0)
f = c;
previousOp = op;
} while (data[0] != '"');
showPath(fPath);
return data;
}
const char pathPrefix[] = "<path fill=\"";
void parseSVG();
void parseSVG() {
const char* data = logoStr;
const char* dataEnd = logoStr + logoStrLen - 1;
while (data < dataEnd) {
SkASSERT(strncmp(data, pathPrefix, sizeof(pathPrefix) - 1) == 0);
data += sizeof(pathPrefix) - 1;
SkDebugf("paint.setColor(0xFF%c%c%c%c%c%c);\n", data[1], data[2], data[3], data[4],
data[5], data[6]);
data += 8;
SkASSERT(strncmp(data, "d=\"", 3) == 0);
data += 3;
SkDebugf("path.reset();\n");
data = parsePath(data);
SkDebugf("canvas->drawPath(path, paint);\n");
SkASSERT(strncmp(data, "\"/>", 3) == 0);
data += 3;
}
}

View File

@ -1,99 +0,0 @@
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "ShapeOps.h"
bool gShowOriginal = true;
struct curve {
SkPath::Verb verb;
SkPoint pts[4];
};
struct curve test1[] = {
{SkPath::kQuad_Verb, {{366.608826f, 151.196014f}, {378.803101f, 136.674606f}, {398.164948f, 136.674606f}}},
{SkPath::kLine_Verb, {{354.009216f, 208.816208f}, {393.291473f, 102.232819f}}},
{SkPath::kQuad_Verb, {{359.978058f, 136.581512f}, {378.315979f, 136.581512f}, {388.322723f, 149.613556f}}},
{SkPath::kQuad_Verb, {{364.390686f, 157.898193f}, {375.281769f, 136.674606f}, {396.039917f, 136.674606f}}},
{SkPath::kLine_Verb, {{396.039917f, 136.674606f}, {350, 120}}},
{SkPath::kDone_Verb}
};
struct curve test2[] = {
{SkPath::kQuad_Verb, {{366.608826f, 151.196014f}, {378.803101f, 136.674606f}, {398.164948f, 136.674606f}}},
{SkPath::kQuad_Verb, {{359.978058f, 136.581512f}, {378.315979f, 136.581512f}, {388.322723f, 149.613556f}}},
{SkPath::kQuad_Verb, {{364.390686f, 157.898193f}, {375.281769f, 136.674606f}, {396.039917f, 136.674606f}}},
{SkPath::kDone_Verb}
};
struct curve* testSet[] = {
test2,
test1
};
size_t testSet_count = sizeof(testSet) / sizeof(testSet[0]);
static void construct() {
for (size_t idx = 0; idx < testSet_count; ++idx) {
const curve* test = testSet[idx];
SkPath path;
bool pathComplete = false;
bool first = true;
do {
if (first) {
path.moveTo(test->pts[0].fX, test->pts[0].fY);
first = false;
} else if (test->verb != SkPath::kDone_Verb) {
path.lineTo(test->pts[0].fX, test->pts[0].fY);
}
switch (test->verb) {
case SkPath::kDone_Verb:
pathComplete = true;
break;
case SkPath::kLine_Verb:
path.lineTo(test->pts[1].fX, test->pts[1].fY);
break;
case SkPath::kQuad_Verb:
path.quadTo(test->pts[1].fX, test->pts[1].fY, test->pts[2].fX, test->pts[2].fY);
break;
case SkPath::kCubic_Verb:
path.cubicTo(test->pts[1].fX, test->pts[1].fY, test->pts[2].fX, test->pts[2].fY, test->pts[3].fX, test->pts[3].fY);
break;
default:
SkASSERT(0);
}
test++;
} while (!pathComplete);
path.close();
if (gShowOriginal) {
showPath(path, NULL);
SkDebugf("simplified:\n");
}
testSimplifyx(path);
}
}
static void (*tests[])() = {
construct,
};
static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
static void (*firstTest)() = 0;
static bool skipAll = false;
void MiniSimplify_Test() {
if (skipAll) {
return;
}
size_t index = 0;
if (firstTest) {
while (index < testCount && tests[index] != firstTest) {
++index;
}
}
bool firstTestComplete = false;
for ( ; index < testCount; ++index) {
(*tests[index])();
firstTestComplete = true;
}
}

View File

@ -1,473 +0,0 @@
/*
Solving the Nearest Point-on-Curve Problem
and
A Bezier Curve-Based Root-Finder
by Philip J. Schneider
from "Graphics Gems", Academic Press, 1990
*/
/* point_on_curve.c */
#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include "GraphicsGems.h"
#define TESTMODE
/*
* Forward declarations
*/
Point2 NearestPointOnCurve();
static int FindRoots();
static Point2 *ConvertToBezierForm();
static double ComputeXIntercept();
static int ControlPolygonFlatEnough();
static int CrossingCount();
static Point2 Bezier();
static Vector2 V2ScaleII();
int MAXDEPTH = 64; /* Maximum depth for recursion */
#define EPSILON (ldexp(1.0,-MAXDEPTH-1)) /*Flatness control value */
#define DEGREE 3 /* Cubic Bezier curve */
#define W_DEGREE 5 /* Degree of eqn to find roots of */
#ifdef TESTMODE
/*
* main :
* Given a cubic Bezier curve (i.e., its control points), and some
* arbitrary point in the plane, find the point on the curve
* closest to that arbitrary point.
*/
main()
{
static Point2 bezCurve[4] = { /* A cubic Bezier curve */
{ 0.0, 0.0 },
{ 1.0, 2.0 },
{ 3.0, 3.0 },
{ 4.0, 2.0 },
};
static Point2 arbPoint = { 3.5, 2.0 }; /*Some arbitrary point*/
Point2 pointOnCurve; /* Nearest point on the curve */
/* Find the closest point */
pointOnCurve = NearestPointOnCurve(arbPoint, bezCurve);
printf("pointOnCurve : (%4.4f, %4.4f)\n", pointOnCurve.x,
pointOnCurve.y);
}
#endif /* TESTMODE */
/*
* NearestPointOnCurve :
* Compute the parameter value of the point on a Bezier
* curve segment closest to some arbtitrary, user-input point.
* Return the point on the curve at that parameter value.
*
*/
Point2 NearestPointOnCurve(P, V)
Point2 P; /* The user-supplied point */
Point2 *V; /* Control points of cubic Bezier */
{
Point2 *w; /* Ctl pts for 5th-degree eqn */
double t_candidate[W_DEGREE]; /* Possible roots */
int n_solutions; /* Number of roots found */
double t; /* Parameter value of closest pt*/
/* Convert problem to 5th-degree Bezier form */
w = ConvertToBezierForm(P, V);
/* Find all possible roots of 5th-degree equation */
n_solutions = FindRoots(w, W_DEGREE, t_candidate, 0);
free((char *)w);
/* Compare distances of P to all candidates, and to t=0, and t=1 */
{
double dist, new_dist;
Point2 p;
Vector2 v;
int i;
/* Check distance to beginning of curve, where t = 0 */
dist = V2SquaredLength(V2Sub(&P, &V[0], &v));
t = 0.0;
/* Find distances for candidate points */
for (i = 0; i < n_solutions; i++) {
p = Bezier(V, DEGREE, t_candidate[i],
(Point2 *)NULL, (Point2 *)NULL);
new_dist = V2SquaredLength(V2Sub(&P, &p, &v));
if (new_dist < dist) {
dist = new_dist;
t = t_candidate[i];
}
}
/* Finally, look at distance to end point, where t = 1.0 */
new_dist = V2SquaredLength(V2Sub(&P, &V[DEGREE], &v));
if (new_dist < dist) {
dist = new_dist;
t = 1.0;
}
}
/* Return the point on the curve at parameter value t */
printf("t : %4.12f\n", t);
return (Bezier(V, DEGREE, t, (Point2 *)NULL, (Point2 *)NULL));
}
/*
* ConvertToBezierForm :
* Given a point and a Bezier curve, generate a 5th-degree
* Bezier-format equation whose solution finds the point on the
* curve nearest the user-defined point.
*/
static Point2 *ConvertToBezierForm(P, V)
Point2 P; /* The point to find t for */
Point2 *V; /* The control points */
{
int i, j, k, m, n, ub, lb;
int row, column; /* Table indices */
Vector2 c[DEGREE+1]; /* V(i)'s - P */
Vector2 d[DEGREE]; /* V(i+1) - V(i) */
Point2 *w; /* Ctl pts of 5th-degree curve */
double cdTable[3][4]; /* Dot product of c, d */
static double z[3][4] = { /* Precomputed "z" for cubics */
{1.0, 0.6, 0.3, 0.1},
{0.4, 0.6, 0.6, 0.4},
{0.1, 0.3, 0.6, 1.0},
};
/*Determine the c's -- these are vectors created by subtracting*/
/* point P from each of the control points */
for (i = 0; i <= DEGREE; i++) {
V2Sub(&V[i], &P, &c[i]);
}
/* Determine the d's -- these are vectors created by subtracting*/
/* each control point from the next */
for (i = 0; i <= DEGREE - 1; i++) {
d[i] = V2ScaleII(V2Sub(&V[i+1], &V[i], &d[i]), 3.0);
}
/* Create the c,d table -- this is a table of dot products of the */
/* c's and d's */
for (row = 0; row <= DEGREE - 1; row++) {
for (column = 0; column <= DEGREE; column++) {
cdTable[row][column] = V2Dot(&d[row], &c[column]);
}
}
/* Now, apply the z's to the dot products, on the skew diagonal*/
/* Also, set up the x-values, making these "points" */
w = (Point2 *)malloc((unsigned)(W_DEGREE+1) * sizeof(Point2));
for (i = 0; i <= W_DEGREE; i++) {
w[i].y = 0.0;
w[i].x = (double)(i) / W_DEGREE;
}
n = DEGREE;
m = DEGREE-1;
for (k = 0; k <= n + m; k++) {
lb = MAX(0, k - m);
ub = MIN(k, n);
for (i = lb; i <= ub; i++) {
j = k - i;
w[i+j].y += cdTable[j][i] * z[j][i];
}
}
return (w);
}
/*
* FindRoots :
* Given a 5th-degree equation in Bernstein-Bezier form, find
* all of the roots in the interval [0, 1]. Return the number
* of roots found.
*/
static int FindRoots(w, degree, t, depth)
Point2 *w; /* The control points */
int degree; /* The degree of the polynomial */
double *t; /* RETURN candidate t-values */
int depth; /* The depth of the recursion */
{
int i;
Point2 Left[W_DEGREE+1], /* New left and right */
Right[W_DEGREE+1]; /* control polygons */
int left_count, /* Solution count from */
right_count; /* children */
double left_t[W_DEGREE+1], /* Solutions from kids */
right_t[W_DEGREE+1];
switch (CrossingCount(w, degree)) {
case 0 : { /* No solutions here */
return 0;
}
case 1 : { /* Unique solution */
/* Stop recursion when the tree is deep enough */
/* if deep enough, return 1 solution at midpoint */
if (depth >= MAXDEPTH) {
t[0] = (w[0].x + w[W_DEGREE].x) / 2.0;
return 1;
}
if (ControlPolygonFlatEnough(w, degree)) {
t[0] = ComputeXIntercept(w, degree);
return 1;
}
break;
}
}
/* Otherwise, solve recursively after */
/* subdividing control polygon */
Bezier(w, degree, 0.5, Left, Right);
left_count = FindRoots(Left, degree, left_t, depth+1);
right_count = FindRoots(Right, degree, right_t, depth+1);
/* Gather solutions together */
for (i = 0; i < left_count; i++) {
t[i] = left_t[i];
}
for (i = 0; i < right_count; i++) {
t[i+left_count] = right_t[i];
}
/* Send back total number of solutions */
return (left_count+right_count);
}
/*
* CrossingCount :
* Count the number of times a Bezier control polygon
* crosses the 0-axis. This number is >= the number of roots.
*
*/
static int CrossingCount(V, degree)
Point2 *V; /* Control pts of Bezier curve */
int degree; /* Degreee of Bezier curve */
{
int i;
int n_crossings = 0; /* Number of zero-crossings */
int sign, old_sign; /* Sign of coefficients */
sign = old_sign = SGN(V[0].y);
for (i = 1; i <= degree; i++) {
sign = SGN(V[i].y);
if (sign != old_sign) n_crossings++;
old_sign = sign;
}
return n_crossings;
}
/*
* ControlPolygonFlatEnough :
* Check if the control polygon of a Bezier curve is flat enough
* for recursive subdivision to bottom out.
*
*/
static int ControlPolygonFlatEnough(V, degree)
Point2 *V; /* Control points */
int degree; /* Degree of polynomial */
{
int i; /* Index variable */
double *distance; /* Distances from pts to line */
double max_distance_above; /* maximum of these */
double max_distance_below;
double error; /* Precision of root */
double intercept_1,
intercept_2,
left_intercept,
right_intercept;
double a, b, c; /* Coefficients of implicit */
/* eqn for line from V[0]-V[deg]*/
/* Find the perpendicular distance */
/* from each interior control point to */
/* line connecting V[0] and V[degree] */
distance = (double *)malloc((unsigned)(degree + 1) * sizeof(double));
{
double abSquared;
/* Derive the implicit equation for line connecting first *'
/* and last control points */
a = V[0].y - V[degree].y;
b = V[degree].x - V[0].x;
c = V[0].x * V[degree].y - V[degree].x * V[0].y;
abSquared = (a * a) + (b * b);
for (i = 1; i < degree; i++) {
/* Compute distance from each of the points to that line */
distance[i] = a * V[i].x + b * V[i].y + c;
if (distance[i] > 0.0) {
distance[i] = (distance[i] * distance[i]) / abSquared;
}
if (distance[i] < 0.0) {
distance[i] = -((distance[i] * distance[i]) / abSquared);
}
}
}
/* Find the largest distance */
max_distance_above = 0.0;
max_distance_below = 0.0;
for (i = 1; i < degree; i++) {
if (distance[i] < 0.0) {
max_distance_below = MIN(max_distance_below, distance[i]);
};
if (distance[i] > 0.0) {
max_distance_above = MAX(max_distance_above, distance[i]);
}
}
free((char *)distance);
{
double det, dInv;
double a1, b1, c1, a2, b2, c2;
/* Implicit equation for zero line */
a1 = 0.0;
b1 = 1.0;
c1 = 0.0;
/* Implicit equation for "above" line */
a2 = a;
b2 = b;
c2 = c + max_distance_above;
det = a1 * b2 - a2 * b1;
dInv = 1.0/det;
intercept_1 = (b1 * c2 - b2 * c1) * dInv;
/* Implicit equation for "below" line */
a2 = a;
b2 = b;
c2 = c + max_distance_below;
det = a1 * b2 - a2 * b1;
dInv = 1.0/det;
intercept_2 = (b1 * c2 - b2 * c1) * dInv;
}
/* Compute intercepts of bounding box */
left_intercept = MIN(intercept_1, intercept_2);
right_intercept = MAX(intercept_1, intercept_2);
error = 0.5 * (right_intercept-left_intercept);
if (error < EPSILON) {
return 1;
}
else {
return 0;
}
}
/*
* ComputeXIntercept :
* Compute intersection of chord from first control point to last
* with 0-axis.
*
*/
/* NOTE: "T" and "Y" do not have to be computed, and there are many useless
* operations in the following (e.g. "0.0 - 0.0").
*/
static double ComputeXIntercept(V, degree)
Point2 *V; /* Control points */
int degree; /* Degree of curve */
{
double XLK, YLK, XNM, YNM, XMK, YMK;
double det, detInv;
double S, T;
double X, Y;
XLK = 1.0 - 0.0;
YLK = 0.0 - 0.0;
XNM = V[degree].x - V[0].x;
YNM = V[degree].y - V[0].y;
XMK = V[0].x - 0.0;
YMK = V[0].y - 0.0;
det = XNM*YLK - YNM*XLK;
detInv = 1.0/det;
S = (XNM*YMK - YNM*XMK) * detInv;
/* T = (XLK*YMK - YLK*XMK) * detInv; */
X = 0.0 + XLK * S;
/* Y = 0.0 + YLK * S; */
return X;
}
/*
* Bezier :
* Evaluate a Bezier curve at a particular parameter value
* Fill in control points for resulting sub-curves if "Left" and
* "Right" are non-null.
*
*/
static Point2 Bezier(V, degree, t, Left, Right)
int degree; /* Degree of bezier curve */
Point2 *V; /* Control pts */
double t; /* Parameter value */
Point2 *Left; /* RETURN left half ctl pts */
Point2 *Right; /* RETURN right half ctl pts */
{
int i, j; /* Index variables */
Point2 Vtemp[W_DEGREE+1][W_DEGREE+1];
/* Copy control points */
for (j =0; j <= degree; j++) {
Vtemp[0][j] = V[j];
}
/* Triangle computation */
for (i = 1; i <= degree; i++) {
for (j =0 ; j <= degree - i; j++) {
Vtemp[i][j].x =
(1.0 - t) * Vtemp[i-1][j].x + t * Vtemp[i-1][j+1].x;
Vtemp[i][j].y =
(1.0 - t) * Vtemp[i-1][j].y + t * Vtemp[i-1][j+1].y;
}
}
if (Left != NULL) {
for (j = 0; j <= degree; j++) {
Left[j] = Vtemp[j][0];
}
}
if (Right != NULL) {
for (j = 0; j <= degree; j++) {
Right[j] = Vtemp[degree-j][j];
}
}
return (Vtemp[degree][0]);
}
static Vector2 V2ScaleII(v, s)
Vector2 *v;
double s;
{
Vector2 result;
result.x = v->x * s; result.y = v->y * s;
return (result);
}

View File

@ -1,12 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
// utilities used only for unit testing
bool point_on_parameterized_curve(const Cubic& cubic, const _Point& point);
bool point_on_parameterized_line(const _Line& line, const _Point& point);
bool point_on_parameterized_curve(const Quadratic& quad, const _Point& point);

View File

@ -1,82 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "LineParameters.h"
#define DEBUG_BEZIER_CLIP 1
// return false if unable to clip (e.g., unable to create implicit line)
// caller should subdivide, or create degenerate if the values are too small
bool bezier_clip(const Quadratic& q1, const Quadratic& q2, double& minT, double& maxT) {
minT = 1;
maxT = 0;
// determine normalized implicit line equation for pt[0] to pt[3]
// of the form ax + by + c = 0, where a*a + b*b == 1
// find the implicit line equation parameters
LineParameters endLine;
endLine.quadEndPoints(q1);
if (!endLine.normalize()) {
printf("line cannot be normalized: need more code here\n");
SkASSERT(0);
return false;
}
double distance = endLine.controlPtDistance(q1);
// find fat line
double top = 0;
double bottom = distance / 2; // http://students.cs.byu.edu/~tom/557/text/cic.pdf (7.6)
if (top > bottom) {
SkTSwap(top, bottom);
}
// compute intersecting candidate distance
Quadratic distance2y; // points with X of (0, 1/2, 1)
endLine.quadDistanceY(q2, distance2y);
int flags = 0;
if (approximately_lesser_or_equal(distance2y[0].y, top)) {
flags |= kFindTopMin;
} else if (approximately_greater_or_equal(distance2y[0].y, bottom)) {
flags |= kFindBottomMin;
} else {
minT = 0;
}
if (approximately_lesser_or_equal(distance2y[2].y, top)) {
flags |= kFindTopMax;
} else if (approximately_greater_or_equal(distance2y[2].y, bottom)) {
flags |= kFindBottomMax;
} else {
maxT = 1;
}
// Find the intersection of distance convex hull and fat line.
int idx = 0;
do {
int next = idx + 1;
if (next == 3) {
next = 0;
}
x_at(distance2y[idx], distance2y[next], top, bottom, flags, minT, maxT);
idx = next;
} while (idx);
#if DEBUG_BEZIER_CLIP
_Rect r1, r2;
r1.setBounds(q1);
r2.setBounds(q2);
_Point testPt = {0.487, 0.337};
if (r1.contains(testPt) && r2.contains(testPt)) {
printf("%s q1=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
" q2=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) minT=%1.9g maxT=%1.9g\n",
__FUNCTION__, q1[0].x, q1[0].y, q1[1].x, q1[1].y, q1[2].x, q1[2].y,
q2[0].x, q2[0].y, q2[1].x, q2[1].y, q2[2].x, q2[2].y, minT, maxT);
}
#endif
return minT < maxT; // returns false if distance shows no intersection
}

View File

@ -1,70 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Intersection_Tests.h"
#include "QuadraticIntersection_TestData.h"
static const Quadratic testSet[] = {
// data for oneOffTest
{{8.0000000000000071, 8.0000000000000071},
{8.7289570079366854, 8.7289570079366889},
{9.3914917259458743, 9.0593802763083691}},
{{8.0000000000000142, 8.0000000000000142},
{8.1250000000000107, 8.1250000000000071},
{8.2500000000000071, 8.2187500000000053}},
// data for oneAtEndTest
{{0.91292418204644155, 0.41931201426549197},
{0.70491388044579517, 0.64754305977710236},
{0, 1 }},
{{0.21875, 0.765625 },
{0.125, 0.875 },
{0, 1 }}
};
static void oneAtEndTest() {
const Quadratic& quad1 = testSet[2];
const Quadratic& quad2 = testSet[3];
double minT = 0;
double maxT = 1;
bezier_clip(quad1, quad2, minT, maxT);
}
static void oneOffTest() {
const Quadratic& quad1 = testSet[0];
const Quadratic& quad2 = testSet[1];
double minT = 0;
double maxT = 1;
bezier_clip(quad1, quad2, minT, maxT);
}
static void standardTestCases() {
for (size_t index = 0; index < quadraticTests_count; ++index) {
const Quadratic& quad1 = quadraticTests[index][0];
const Quadratic& quad2 = quadraticTests[index][1];
Quadratic reduce1, reduce2;
int order1 = reduceOrder(quad1, reduce1, kReduceOrder_TreatAsFill);
int order2 = reduceOrder(quad2, reduce2, kReduceOrder_TreatAsFill);
if (order1 < 3) {
SkDebugf("%s [%d] quad1 order=%d\n", __FUNCTION__, (int)index, order1);
}
if (order2 < 3) {
SkDebugf("%s [%d] quad2 order=%d\n", __FUNCTION__, (int)index, order2);
}
if (order1 == 3 && order2 == 3) {
double minT = 0;
double maxT = 1;
bezier_clip(reduce1, reduce2, minT, maxT);
}
}
}
void QuadraticBezierClip_Test() {
oneAtEndTest();
oneOffTest();
standardTestCases();
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Extrema.h"
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 (!between(quad[0].x, quad[1].x, quad[2].x)) {
roots = findExtrema(quad[0].x, quad[1].x, quad[2].x, tValues);
}
if (!between(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]);
}
}

View File

@ -1,572 +0,0 @@
// Another approach is to start with the implicit form of one curve and solve
// (seek implicit coefficients in QuadraticParameter.cpp
// by substituting in the parametric form of the other.
// The downside of this approach is that early rejects are difficult to come by.
// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html#step
#include "CubicUtilities.h"
#include "CurveIntersection.h"
#include "Intersections.h"
#include "QuadraticParameterization.h"
#include "QuarticRoot.h"
#include "QuadraticUtilities.h"
#include "TSearch.h"
#ifdef SK_DEBUG
#include "LineUtilities.h"
#endif
/* given the implicit form 0 = Ax^2 + Bxy + Cy^2 + Dx + Ey + F
* and given x = at^2 + bt + c (the parameterized form)
* y = dt^2 + et + f
* then
* 0 = A(at^2+bt+c)(at^2+bt+c)+B(at^2+bt+c)(dt^2+et+f)+C(dt^2+et+f)(dt^2+et+f)+D(at^2+bt+c)+E(dt^2+et+f)+F
*/
static int findRoots(const QuadImplicitForm& i, const Quadratic& q2, double roots[4],
bool oneHint, int firstCubicRoot) {
double a, b, c;
set_abc(&q2[0].x, a, b, c);
double d, e, f;
set_abc(&q2[0].y, d, e, f);
const double t4 = i.x2() * a * a
+ i.xy() * a * d
+ i.y2() * d * d;
const double t3 = 2 * i.x2() * a * b
+ i.xy() * (a * e + b * d)
+ 2 * i.y2() * d * e;
const double t2 = i.x2() * (b * b + 2 * a * c)
+ i.xy() * (c * d + b * e + a * f)
+ i.y2() * (e * e + 2 * d * f)
+ i.x() * a
+ i.y() * d;
const double t1 = 2 * i.x2() * b * c
+ i.xy() * (c * e + b * f)
+ 2 * i.y2() * e * f
+ i.x() * b
+ i.y() * e;
const double t0 = i.x2() * c * c
+ i.xy() * c * f
+ i.y2() * f * f
+ i.x() * c
+ i.y() * f
+ i.c();
int rootCount = reducedQuarticRoots(t4, t3, t2, t1, t0, oneHint, roots);
if (rootCount >= 0) {
return rootCount;
}
return quarticRootsReal(firstCubicRoot, t4, t3, t2, t1, t0, roots);
}
static int addValidRoots(const double roots[4], const int count, double valid[4]) {
int result = 0;
int index;
for (index = 0; index < count; ++index) {
if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_less(roots[index])) {
continue;
}
double t = 1 - roots[index];
if (approximately_less_than_zero(t)) {
t = 0;
} else if (approximately_greater_than_one(t)) {
t = 1;
}
valid[result++] = t;
}
return result;
}
static bool onlyEndPtsInCommon(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
// the idea here is to see at minimum do a quick reject by rotating all points
// to either side of the line formed by connecting the endpoints
// if the opposite curves points are on the line or on the other side, the
// curves at most intersect at the endpoints
for (int oddMan = 0; oddMan < 3; ++oddMan) {
const _Point* endPt[2];
for (int opp = 1; opp < 3; ++opp) {
int end = oddMan ^ opp;
if (end == 3) {
end = opp;
}
endPt[opp - 1] = &q1[end];
}
double origX = endPt[0]->x;
double origY = endPt[0]->y;
double adj = endPt[1]->x - origX;
double opp = endPt[1]->y - origY;
double sign = (q1[oddMan].y - origY) * adj - (q1[oddMan].x - origX) * opp;
if (approximately_zero(sign)) {
goto tryNextHalfPlane;
}
for (int n = 0; n < 3; ++n) {
double test = (q2[n].y - origY) * adj - (q2[n].x - origX) * opp;
if (test * sign > 0) {
goto tryNextHalfPlane;
}
}
for (int i1 = 0; i1 < 3; i1 += 2) {
for (int i2 = 0; i2 < 3; i2 += 2) {
if (q1[i1] == q2[i2]) {
i.insert(i1 >> 1, i2 >> 1, q1[i1]);
}
}
}
SkASSERT(i.fUsed < 3);
return true;
tryNextHalfPlane:
;
}
return false;
}
// returns false if there's more than one intercept or the intercept doesn't match the point
// returns true if the intercept was successfully added or if the
// original quads need to be subdivided
static bool addIntercept(const Quadratic& q1, const Quadratic& q2, double tMin, double tMax,
Intersections& i, bool* subDivide) {
double tMid = (tMin + tMax) / 2;
_Point mid;
xy_at_t(q2, tMid, mid.x, mid.y);
_Line line;
line[0] = line[1] = mid;
_Vector dxdy = dxdy_at_t(q2, tMid);
line[0] -= dxdy;
line[1] += dxdy;
Intersections rootTs;
int roots = intersect(q1, line, rootTs);
if (roots == 0) {
if (subDivide) {
*subDivide = true;
}
return true;
}
if (roots == 2) {
return false;
}
_Point pt2;
xy_at_t(q1, rootTs.fT[0][0], pt2.x, pt2.y);
if (!pt2.approximatelyEqualHalf(mid)) {
return false;
}
i.insertSwap(rootTs.fT[0][0], tMid, pt2);
return true;
}
static bool isLinearInner(const Quadratic& q1, double t1s, double t1e, const Quadratic& q2,
double t2s, double t2e, Intersections& i, bool* subDivide) {
Quadratic hull;
sub_divide(q1, t1s, t1e, hull);
_Line line = {hull[2], hull[0]};
const _Line* testLines[] = { &line, (const _Line*) &hull[0], (const _Line*) &hull[1] };
size_t testCount = sizeof(testLines) / sizeof(testLines[0]);
SkTDArray<double> tsFound;
for (size_t index = 0; index < testCount; ++index) {
Intersections rootTs;
int roots = intersect(q2, *testLines[index], rootTs);
for (int idx2 = 0; idx2 < roots; ++idx2) {
double t = rootTs.fT[0][idx2];
#ifdef SK_DEBUG
_Point qPt, lPt;
xy_at_t(q2, t, qPt.x, qPt.y);
xy_at_t(*testLines[index], rootTs.fT[1][idx2], lPt.x, lPt.y);
SkASSERT(qPt.approximatelyEqual(lPt));
#endif
if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) {
continue;
}
*tsFound.append() = rootTs.fT[0][idx2];
}
}
int tCount = tsFound.count();
if (!tCount) {
return true;
}
double tMin, tMax;
if (tCount == 1) {
tMin = tMax = tsFound[0];
} else if (tCount > 1) {
QSort<double>(tsFound.begin(), tsFound.end() - 1);
tMin = tsFound[0];
tMax = tsFound[tsFound.count() - 1];
}
_Point end;
xy_at_t(q2, t2s, end.x, end.y);
bool startInTriangle = point_in_hull(hull, end);
if (startInTriangle) {
tMin = t2s;
}
xy_at_t(q2, t2e, end.x, end.y);
bool endInTriangle = point_in_hull(hull, end);
if (endInTriangle) {
tMax = t2e;
}
int split = 0;
_Vector dxy1, dxy2;
if (tMin != tMax || tCount > 2) {
dxy2 = dxdy_at_t(q2, tMin);
for (int index = 1; index < tCount; ++index) {
dxy1 = dxy2;
dxy2 = dxdy_at_t(q2, tsFound[index]);
double dot = dxy1.dot(dxy2);
if (dot < 0) {
split = index - 1;
break;
}
}
}
if (split == 0) { // there's one point
if (addIntercept(q1, q2, tMin, tMax, i, subDivide)) {
return true;
}
i.swap();
return isLinearInner(q2, tMin, tMax, q1, t1s, t1e, i, subDivide);
}
// At this point, we have two ranges of t values -- treat each separately at the split
bool result;
if (addIntercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) {
result = true;
} else {
i.swap();
result = isLinearInner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide);
}
if (addIntercept(q1, q2, tsFound[split], tMax, i, subDivide)) {
result = true;
} else {
i.swap();
result |= isLinearInner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide);
}
return result;
}
static double flatMeasure(const Quadratic& q) {
_Vector mid = q[1] - q[0];
_Vector dxy = q[2] - q[0];
double length = dxy.length(); // OPTIMIZE: get rid of sqrt
return fabs(mid.cross(dxy) / length);
}
// FIXME ? should this measure both and then use the quad that is the flattest as the line?
static bool isLinear(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
double measure = flatMeasure(q1);
// OPTIMIZE: (get rid of sqrt) use approximately_zero
if (!approximately_zero_sqrt(measure)) {
return false;
}
return isLinearInner(q1, 0, 1, q2, 0, 1, i, NULL);
}
// FIXME: if flat measure is sufficiently large, then probably the quartic solution failed
static void relaxedIsLinear(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
double m1 = flatMeasure(q1);
double m2 = flatMeasure(q2);
#ifdef SK_DEBUG
double min = SkTMin(m1, m2);
if (min > 5) {
SkDebugf("%s maybe not flat enough.. %1.9g\n", __FUNCTION__, min);
}
#endif
i.reset();
const Quadratic& rounder = m2 < m1 ? q1 : q2;
const Quadratic& flatter = m2 < m1 ? q2 : q1;
bool subDivide = false;
isLinearInner(flatter, 0, 1, rounder, 0, 1, i, &subDivide);
if (subDivide) {
QuadraticPair pair;
chop_at(flatter, pair, 0.5);
Intersections firstI, secondI;
relaxedIsLinear(pair.first(), rounder, firstI);
for (int index = 0; index < firstI.used(); ++index) {
i.insert(firstI.fT[0][index] * 0.5, firstI.fT[1][index], firstI.fPt[index]);
}
relaxedIsLinear(pair.second(), rounder, secondI);
for (int index = 0; index < secondI.used(); ++index) {
i.insert(0.5 + secondI.fT[0][index] * 0.5, secondI.fT[1][index], secondI.fPt[index]);
}
}
if (m2 < m1) {
i.swapPts();
}
}
#if 0
static void unsortableExpanse(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
const Quadratic* qs[2] = { &q1, &q2 };
// need t values for start and end of unsortable expanse on both curves
// try projecting lines parallel to the end points
i.fT[0][0] = 0;
i.fT[0][1] = 1;
int flip = -1; // undecided
for (int qIdx = 0; qIdx < 2; qIdx++) {
for (int t = 0; t < 2; t++) {
_Point dxdy;
dxdy_at_t(*qs[qIdx], t, dxdy);
_Line perp;
perp[0] = perp[1] = (*qs[qIdx])[t == 0 ? 0 : 2];
perp[0].x += dxdy.y;
perp[0].y -= dxdy.x;
perp[1].x -= dxdy.y;
perp[1].y += dxdy.x;
Intersections hitData;
int hits = intersectRay(*qs[qIdx ^ 1], perp, hitData);
SkASSERT(hits <= 1);
if (hits) {
if (flip < 0) {
_Point dxdy2;
dxdy_at_t(*qs[qIdx ^ 1], hitData.fT[0][0], dxdy2);
double dot = dxdy.dot(dxdy2);
flip = dot < 0;
i.fT[1][0] = flip;
i.fT[1][1] = !flip;
}
i.fT[qIdx ^ 1][t ^ flip] = hitData.fT[0][0];
}
}
}
i.fUnsortable = true; // failed, probably coincident or near-coincident
i.fUsed = 2;
}
#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) {
// if the quads share an end point, check to see if they overlap
if (onlyEndPtsInCommon(q1, q2, i)) {
return i.intersected();
}
if (onlyEndPtsInCommon(q2, q1, i)) {
i.swapPts();
return i.intersected();
}
// see if either quad is really a line
if (isLinear(q1, q2, i)) {
return i.intersected();
}
if (isLinear(q2, q1, i)) {
i.swapPts();
return i.intersected();
}
QuadImplicitForm i1(q1);
QuadImplicitForm i2(q2);
if (i1.implicit_match(i2)) {
// 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.insertCoincident(t, 0, q2[0]);
}
if ((t = axialIntersect(q1, q2[2], useVertical)) >= 0) {
i.insertCoincident(t, 1, q2[2]);
}
useVertical = fabs(q2[0].x - q2[2].x) < fabs(q2[0].y - q2[2].y);
if ((t = axialIntersect(q2, q1[0], useVertical)) >= 0) {
i.insertCoincident(0, t, q1[0]);
}
if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) {
i.insertCoincident(1, t, q1[2]);
}
SkASSERT(i.coincidentUsed() <= 2);
return i.coincidentUsed() > 0;
}
int index;
bool useCubic = q1[0] == q2[0] || q1[0] == q2[2] || q1[2] == q2[0];
double roots1[4];
int rootCount = findRoots(i2, q1, roots1, useCubic, 0);
// OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
double roots1Copy[4];
int r1Count = addValidRoots(roots1, rootCount, roots1Copy);
_Point pts1[4];
for (index = 0; index < r1Count; ++index) {
xy_at_t(q1, roots1Copy[index], pts1[index].x, pts1[index].y);
}
double roots2[4];
int rootCount2 = findRoots(i1, q2, roots2, useCubic, 0);
double roots2Copy[4];
int r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
_Point pts2[4];
for (index = 0; index < r2Count; ++index) {
xy_at_t(q2, roots2Copy[index], pts2[index].x, pts2[index].y);
}
if (r1Count == r2Count && r1Count <= 1) {
if (r1Count == 1) {
if (pts1[0].approximatelyEqualHalf(pts2[0])) {
i.insert(roots1Copy[0], roots2Copy[0], pts1[0]);
} else if (pts1[0].moreRoughlyEqual(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();
}
int closest[4];
double dist[4];
bool foundSomething = false;
for (index = 0; index < r1Count; ++index) {
dist[index] = DBL_MAX;
closest[index] = -1;
for (int ndex2 = 0; ndex2 < r2Count; ++ndex2) {
if (!pts2[ndex2].approximatelyEqualHalf(pts1[index])) {
continue;
}
double dx = pts2[ndex2].x - pts1[index].x;
double dy = pts2[ndex2].y - pts1[index].y;
double distance = dx * dx + dy * dy;
if (dist[index] <= distance) {
continue;
}
for (int outer = 0; outer < index; ++outer) {
if (closest[outer] != ndex2) {
continue;
}
if (dist[outer] < distance) {
goto next;
}
closest[outer] = -1;
}
dist[index] = distance;
closest[index] = ndex2;
foundSomething = true;
next:
;
}
}
if (r1Count && r2Count && !foundSomething) {
relaxedIsLinear(q1, q2, i);
return i.intersected();
}
int used = 0;
do {
double lowest = DBL_MAX;
int lowestIndex = -1;
for (index = 0; index < r1Count; ++index) {
if (closest[index] < 0) {
continue;
}
if (roots1Copy[index] < lowest) {
lowestIndex = index;
lowest = roots1Copy[index];
}
}
if (lowestIndex < 0) {
break;
}
i.insert(roots1Copy[lowestIndex], roots2Copy[closest[lowestIndex]],
pts1[lowestIndex]);
closest[lowestIndex] = -1;
} while (++used < r1Count);
i.fFlip = false;
return i.intersected();
}

View File

@ -1,403 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Intersections.h"
#include "IntersectionUtilities.h"
#include "LineIntersection.h"
#include "LineUtilities.h"
#include "QuadraticLineSegments.h"
#include "QuadraticUtilities.h"
#include <algorithm> // for swap
static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf see Multiple intersections
class QuadraticIntersections {
public:
QuadraticIntersections(const Quadratic& q1, const Quadratic& q2, Intersections& i)
: quad1(q1)
, quad2(q2)
, intersections(i)
, depth(0)
, splits(0)
, coinMinT1(-1) {
}
bool intersect() {
double minT1, minT2, maxT1, maxT2;
if (!bezier_clip(quad2, quad1, minT1, maxT1)) {
return false;
}
if (!bezier_clip(quad1, quad2, minT2, maxT2)) {
return false;
}
quad1Divisions = 1 / subDivisions(quad1);
quad2Divisions = 1 / subDivisions(quad2);
int split;
if (maxT1 - minT1 < maxT2 - minT2) {
intersections.swap();
minT2 = 0;
maxT2 = 1;
split = maxT1 - minT1 > tClipLimit;
} else {
minT1 = 0;
maxT1 = 1;
split = (maxT2 - minT2 > tClipLimit) << 1;
}
return chop(minT1, maxT1, minT2, maxT2, split);
}
protected:
bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
bool t1IsLine = maxT1 - minT1 <= quad1Divisions;
bool t2IsLine = maxT2 - minT2 <= quad2Divisions;
if (t1IsLine | t2IsLine) {
return intersectAsLine(minT1, maxT1, minT2, maxT2, t1IsLine, t2IsLine);
}
Quadratic smaller, larger;
// FIXME: carry last subdivide and reduceOrder result with quad
sub_divide(quad1, minT1, maxT1, intersections.swapped() ? larger : smaller);
sub_divide(quad2, minT2, maxT2, intersections.swapped() ? smaller : larger);
double minT, maxT;
if (!bezier_clip(smaller, larger, minT, maxT)) {
if (approximately_equal(minT, maxT)) {
double smallT, largeT;
_Point q2pt, q1pt;
if (intersections.swapped()) {
largeT = interp(minT2, maxT2, minT);
xy_at_t(quad2, largeT, q2pt.x, q2pt.y);
xy_at_t(quad1, minT1, q1pt.x, q1pt.y);
if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)) {
smallT = minT1;
} else {
xy_at_t(quad1, maxT1, q1pt.x, q1pt.y); // FIXME: debug code
SkASSERT(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y));
smallT = maxT1;
}
} else {
smallT = interp(minT1, maxT1, minT);
xy_at_t(quad1, smallT, q1pt.x, q1pt.y);
xy_at_t(quad2, minT2, q2pt.x, q2pt.y);
if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)) {
largeT = minT2;
} else {
xy_at_t(quad2, maxT2, q2pt.x, q2pt.y); // FIXME: debug code
SkASSERT(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y));
largeT = maxT2;
}
}
intersections.add(smallT, largeT);
return true;
}
return false;
}
int split;
if (intersections.swapped()) {
double newMinT1 = interp(minT1, maxT1, minT);
double newMaxT1 = interp(minT1, maxT1, maxT);
split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1;
#define VERBOSE 0
#if VERBOSE
printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n", __FUNCTION__, depth,
splits, newMinT1, newMaxT1, minT1, maxT1, split);
#endif
minT1 = newMinT1;
maxT1 = newMaxT1;
} else {
double newMinT2 = interp(minT2, maxT2, minT);
double newMaxT2 = interp(minT2, maxT2, maxT);
split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit;
#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);
#endif
minT2 = newMinT2;
maxT2 = newMaxT2;
}
return chop(minT1, maxT1, minT2, maxT2, split);
}
bool intersectAsLine(double minT1, double maxT1, double minT2, double maxT2,
bool treat1AsLine, bool treat2AsLine)
{
_Line line1, line2;
if (intersections.swapped()) {
SkTSwap(treat1AsLine, treat2AsLine);
SkTSwap(minT1, minT2);
SkTSwap(maxT1, maxT2);
}
if (coinMinT1 >= 0) {
bool earlyExit;
if ((earlyExit = coinMaxT1 == minT1)) {
coinMaxT1 = maxT1;
}
if (coinMaxT2 == minT2) {
coinMaxT2 = maxT2;
return true;
}
if (earlyExit) {
return true;
}
coinMinT1 = -1;
}
// do line/quadratic or even line/line intersection instead
if (treat1AsLine) {
xy_at_t(quad1, minT1, line1[0].x, line1[0].y);
xy_at_t(quad1, maxT1, line1[1].x, line1[1].y);
}
if (treat2AsLine) {
xy_at_t(quad2, minT2, line2[0].x, line2[0].y);
xy_at_t(quad2, maxT2, line2[1].x, line2[1].y);
}
int pts;
double smallT1, largeT1, smallT2, largeT2;
if (treat1AsLine & treat2AsLine) {
double t1[2], t2[2];
pts = ::intersect(line1, line2, t1, t2);
if (pts == 2) {
smallT1 = interp(minT1, maxT1, t1[0]);
largeT1 = interp(minT2, maxT2, t2[0]);
smallT2 = interp(minT1, maxT1, t1[1]);
largeT2 = interp(minT2, maxT2, t2[1]);
intersections.addCoincident(smallT1, smallT2, largeT1, largeT2);
} else {
smallT1 = interp(minT1, maxT1, t1[0]);
largeT1 = interp(minT2, maxT2, t2[0]);
intersections.add(smallT1, largeT1);
}
} else {
Intersections lq;
pts = ::intersect(treat1AsLine ? quad2 : quad1,
treat1AsLine ? line1 : line2, lq);
if (pts == 2) { // if the line and edge are coincident treat differently
_Point midQuad, midLine;
double midQuadT = (lq.fT[0][0] + lq.fT[0][1]) / 2;
xy_at_t(treat1AsLine ? quad2 : quad1, midQuadT, midQuad.x, midQuad.y);
double lineT = t_at(treat1AsLine ? line1 : line2, midQuad);
xy_at_t(treat1AsLine ? line1 : line2, lineT, midLine.x, midLine.y);
if (AlmostEqualUlps(midQuad.x, midLine.x)
&& AlmostEqualUlps(midQuad.y, midLine.y)) {
smallT1 = lq.fT[0][0];
largeT1 = lq.fT[1][0];
smallT2 = lq.fT[0][1];
largeT2 = lq.fT[1][1];
if (treat2AsLine) {
smallT1 = interp(minT1, maxT1, smallT1);
smallT2 = interp(minT1, maxT1, smallT2);
} else {
largeT1 = interp(minT2, maxT2, largeT1);
largeT2 = interp(minT2, maxT2, largeT2);
}
intersections.addCoincident(smallT1, smallT2, largeT1, largeT2);
goto setCoinMinMax;
}
}
for (int index = 0; index < pts; ++index) {
smallT1 = lq.fT[0][index];
largeT1 = lq.fT[1][index];
if (treat2AsLine) {
smallT1 = interp(minT1, maxT1, smallT1);
} else {
largeT1 = interp(minT2, maxT2, largeT1);
}
intersections.add(smallT1, largeT1);
}
}
if (pts > 0) {
setCoinMinMax:
coinMinT1 = minT1;
coinMaxT1 = maxT1;
coinMinT2 = minT2;
coinMaxT2 = maxT2;
}
return pts > 0;
}
bool chop(double minT1, double maxT1, double minT2, double maxT2, int split) {
++depth;
intersections.swap();
if (split) {
++splits;
if (split & 2) {
double middle1 = (maxT1 + minT1) / 2;
intersect(minT1, middle1, minT2, maxT2);
intersect(middle1, maxT1, minT2, maxT2);
} else {
double middle2 = (maxT2 + minT2) / 2;
intersect(minT1, maxT1, minT2, middle2);
intersect(minT1, maxT1, middle2, maxT2);
}
--splits;
intersections.swap();
--depth;
return intersections.intersected();
}
bool result = intersect(minT1, maxT1, minT2, maxT2);
intersections.swap();
--depth;
return result;
}
private:
const Quadratic& quad1;
const Quadratic& quad2;
Intersections& intersections;
int depth;
int splits;
double quad1Divisions; // line segments to approximate original within error
double quad2Divisions;
double coinMinT1; // range of Ts where approximate line intersected curve
double coinMaxT1;
double coinMinT2;
double coinMaxT2;
};
#include "LineParameters.h"
static void hackToFixPartialCoincidence(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
// look to see if non-coincident data basically has unsortable tangents
// look to see if a point between non-coincident data is on the curve
int cIndex;
for (int uIndex = 0; uIndex < i.fUsed; ) {
double bestDist1 = 1;
double bestDist2 = 1;
int closest1 = -1;
int closest2 = -1;
for (cIndex = 0; cIndex < i.fCoincidentUsed; ++cIndex) {
double dist = fabs(i.fT[0][uIndex] - i.fCoincidentT[0][cIndex]);
if (bestDist1 > dist) {
bestDist1 = dist;
closest1 = cIndex;
}
dist = fabs(i.fT[1][uIndex] - i.fCoincidentT[1][cIndex]);
if (bestDist2 > dist) {
bestDist2 = dist;
closest2 = cIndex;
}
}
_Line ends;
_Point mid;
double t1 = i.fT[0][uIndex];
xy_at_t(q1, t1, ends[0].x, ends[0].y);
xy_at_t(q1, i.fCoincidentT[0][closest1], ends[1].x, ends[1].y);
double midT = (t1 + i.fCoincidentT[0][closest1]) / 2;
xy_at_t(q1, midT, mid.x, mid.y);
LineParameters params;
params.lineEndPoints(ends);
double midDist = params.pointDistance(mid);
// Note that we prefer to always measure t error, which does not scale,
// instead of point error, which is scale dependent. FIXME
if (!approximately_zero(midDist)) {
++uIndex;
continue;
}
double t2 = i.fT[1][uIndex];
xy_at_t(q2, t2, ends[0].x, ends[0].y);
xy_at_t(q2, i.fCoincidentT[1][closest2], ends[1].x, ends[1].y);
midT = (t2 + i.fCoincidentT[1][closest2]) / 2;
xy_at_t(q2, midT, mid.x, mid.y);
params.lineEndPoints(ends);
midDist = params.pointDistance(mid);
if (!approximately_zero(midDist)) {
++uIndex;
continue;
}
// if both midpoints are close to the line, lengthen coincident span
int cEnd = closest1 ^ 1; // assume coincidence always travels in pairs
if (!between(i.fCoincidentT[0][cEnd], t1, i.fCoincidentT[0][closest1])) {
i.fCoincidentT[0][closest1] = t1;
}
cEnd = closest2 ^ 1;
if (!between(i.fCoincidentT[0][cEnd], t2, i.fCoincidentT[0][closest2])) {
i.fCoincidentT[0][closest2] = t2;
}
int remaining = --i.fUsed - uIndex;
if (remaining > 0) {
memmove(&i.fT[0][uIndex], &i.fT[0][uIndex + 1], sizeof(i.fT[0][0]) * remaining);
memmove(&i.fT[1][uIndex], &i.fT[1][uIndex + 1], sizeof(i.fT[1][0]) * remaining);
}
}
// if coincident data is subjectively a tiny span, replace it with a single point
for (cIndex = 0; cIndex < i.fCoincidentUsed; ) {
double start1 = i.fCoincidentT[0][cIndex];
double end1 = i.fCoincidentT[0][cIndex + 1];
_Line ends1;
xy_at_t(q1, start1, ends1[0].x, ends1[0].y);
xy_at_t(q1, end1, ends1[1].x, ends1[1].y);
if (!AlmostEqualUlps(ends1[0].x, ends1[1].x) || AlmostEqualUlps(ends1[0].y, ends1[1].y)) {
cIndex += 2;
continue;
}
double start2 = i.fCoincidentT[1][cIndex];
double end2 = i.fCoincidentT[1][cIndex + 1];
_Line ends2;
xy_at_t(q2, start2, ends2[0].x, ends2[0].y);
xy_at_t(q2, end2, ends2[1].x, ends2[1].y);
// again, approximately should be used with T values, not points FIXME
if (!AlmostEqualUlps(ends2[0].x, ends2[1].x) || AlmostEqualUlps(ends2[0].y, ends2[1].y)) {
cIndex += 2;
continue;
}
if (approximately_less_than_zero(start1) || approximately_less_than_zero(end1)) {
start1 = 0;
} else if (approximately_greater_than_one(start1) || approximately_greater_than_one(end1)) {
start1 = 1;
} else {
start1 = (start1 + end1) / 2;
}
if (approximately_less_than_zero(start2) || approximately_less_than_zero(end2)) {
start2 = 0;
} else if (approximately_greater_than_one(start2) || approximately_greater_than_one(end2)) {
start2 = 1;
} else {
start2 = (start2 + end2) / 2;
}
i.insert(start1, start2);
i.fCoincidentUsed -= 2;
int remaining = i.fCoincidentUsed - cIndex;
if (remaining > 0) {
memmove(&i.fCoincidentT[0][cIndex], &i.fCoincidentT[0][cIndex + 2], sizeof(i.fCoincidentT[0][0]) * remaining);
memmove(&i.fCoincidentT[1][cIndex], &i.fCoincidentT[1][cIndex + 2], sizeof(i.fCoincidentT[1][0]) * remaining);
}
}
}
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.addCoincident(t, 0);
}
if ((t = axialIntersect(q1, q2[2], useVertical)) >= 0) {
i.addCoincident(t, 1);
}
useVertical = fabs(q2[0].x - q2[2].x) < fabs(q2[0].y - q2[2].y);
if ((t = axialIntersect(q2, q1[0], useVertical)) >= 0) {
i.addCoincident(0, t);
}
if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) {
i.addCoincident(1, t);
}
SkASSERT(i.fCoincidentUsed <= 2);
return i.fCoincidentUsed > 0;
}
QuadraticIntersections q(q1, q2, i);
bool result = q.intersect();
// FIXME: partial coincidence detection is currently poor. For now, try
// to fix up the data after the fact. In the future, revisit the error
// term to try to avoid this kind of result in the first place.
if (i.fUsed && i.fCoincidentUsed) {
hackToFixPartialCoincidence(q1, q2, i);
}
return result;
}

View File

@ -1,436 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Intersection_Tests.h"
#include "Intersections.h"
#include "LineIntersection.h"
#include "QuadraticIntersection_TestData.h"
#include "QuadraticUtilities.h"
#include "TestUtilities.h"
const int firstQuadIntersectionTest = 9;
static void standardTestCases() {
for (size_t index = firstQuadIntersectionTest; index < quadraticTests_count; ++index) {
const Quadratic& quad1 = quadraticTests[index][0];
const Quadratic& quad2 = quadraticTests[index][1];
Quadratic reduce1, reduce2;
int order1 = reduceOrder(quad1, reduce1, kReduceOrder_TreatAsFill);
int order2 = reduceOrder(quad2, reduce2, kReduceOrder_TreatAsFill);
if (order1 < 3) {
printf("[%d] quad1 order=%d\n", (int) index, order1);
}
if (order2 < 3) {
printf("[%d] quad2 order=%d\n", (int) index, order2);
}
if (order1 == 3 && order2 == 3) {
Intersections intersections;
intersect2(reduce1, reduce2, intersections);
if (intersections.intersected()) {
for (int pt = 0; pt < intersections.used(); ++pt) {
double tt1 = intersections.fT[0][pt];
double tx1, ty1;
xy_at_t(quad1, tt1, tx1, ty1);
double tt2 = intersections.fT[1][pt];
double tx2, ty2;
xy_at_t(quad2, tt2, tx2, ty2);
if (!approximately_equal(tx1, tx2)) {
printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
__FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
}
if (!approximately_equal(ty1, ty2)) {
printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
__FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
}
}
}
}
}
}
static const Quadratic testSet[] = {
{{3.0774019473063863, 3.35198509346713}, {3.0757503498668397, 3.327320623945933}, {3.0744102085015879, 3.3025879417907196}},
{{3.053913680774329, 3.3310471586283938}, {3.0758730889691694, 3.3273466070370152}, {3.0975671980059394, 3.3235031316554351}},
{{3.39068129,4.44939202}, {3.03659239,3.81843234}, {3.06844529,3.02100922}},
{{2.10714698,3.44196686}, {3.12180288,3.38575704}, {3.75968569,3.1281838}},
{{2.74792918,4.77711896}, {2.82236867,4.23882547}, {2.82848144,3.63729341}},
{{2.62772567,3.64823958}, {3.46652495,3.64258364}, {4.1425079,3.48623815}},
{{1.34375,2.03125}, {2.2734375,2.6640625}, {3.25,3.25}},
{{3.96875,4.65625}, {3.3359375,3.7265625}, {2.75,2.75}},
{{0,1}, {0.324417544,2.27953848}, {0.664376547,2.58940267}},
{{1,2}, {0.62109375,2.70703125}, {0.640625,2.546875}},
{{1,2}, {0.984375,2.3359375}, {1.0625,2.15625}},
{{0,1}, {0.983539095,2.30041152}, {1.47325103,2.61316872}},
{{4.09011926,2.20971038}, {4.74608133,1.9335932}, {5.02469918,2.00694987}},
{{2.79472921,1.73568666}, {3.36246373,1.21251209}, {5,2}},
{{1.80814127,2.41537795}, {2.23475077,2.05922313}, {3.16529668,1.98358763}},
{{2.16505631,2.55782454}, {2.40541285,2.02193091}, {2.99836023,1.68247638}},
{{3, 1.875}, {3.375, 1.54296875}, {3.375, 1.421875}},
{{3.375, 1.421875}, {3.3749999999999996, 1.3007812499999998}, {3, 2}},
{{3.34,8.98}, {2.83363281,9.4265625}, {2.83796875,9.363125}},
{{2.83796875,9.363125}, {2.84230469,9.2996875}, {3.17875,9.1725}},
{{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.27672209,0.15}, {1.32143477,9.25185854e-17}, {1,0}},
{{0, 0}, {0.51851851851851849, 1.0185185185185186}, {1.2592592592592591, 1.9259259259259258}},
{{1.2592592592592593, 1.9259259259259265}, {0.51851851851851893, 1.0185185185185195}, {0, 0}},
{{1.93281168,2.58856757}, {2.38543691,2.7096125}, {2.51967352,2.34531784}},
{{2.51967352,2.34531784}, {2.65263731,2.00639194}, {3.1212119,1.98608967}},
{{2.09544533,2.51981963}, {2.33331524,2.25252128}, {2.92003302,2.39442311}},
{{0.924337655,1.94072717}, {1.25185043,1.52836494}, {1.71793901,1.06149951}},
{{0.940798831,1.67439357}, {1.25988251,1.39778567}, {1.71791672,1.06650313}},
{{0.924337655,1.94072717}, {1.39158994,1.32418496}, {2.14967426,0.687365435}},
{{0.940798831,1.67439357}, {1.48941875,1.16280321}, {2.47884711,0.60465921}},
{{1.7465749139282332,1.9930452039527999}, {1.8320006564080331,1.859481345189089}, {1.8731035127758437,1.6344055934266613}},
{{1.8731035127758437,1.6344055934266613}, {1.89928170345231,1.5006405518943067}, {1.9223833226085514,1.3495796165215643}},
{{1.74657491,1.9930452}, {1.87407679,1.76762853}, {1.92238332,1.34957962}},
{{0.60797907,1.68776977}, {1.0447864,1.50810914}, {1.87464474,1.63655092}},
{{1.87464474,1.63655092}, {2.70450308,1.76499271}, {4,3}},
{{1.2071879545809394,0.82163474041730045}, {1.1534203513372994,0.52790870069930229}, {1.0880000000000001,0.29599999999999982}}, //t=0.63155333662549329,0.80000000000000004
{{0.33333333333333326,0.81481481481481488}, {0.63395173631977997,0.68744136726313931}, {1.205684411948591,0.81344322326274499}},
{{0.33333333333333326,0.81481481481481488}, {0.63396444791444551,0.68743368362444768}, {1.205732763658403,0.81345617746834109}},//t=0.33333333333333331,0.63396444791444551
{{1.205684411948591,0.81344322326274499}, {1.2057085875611198,0.81344969999329253}, {1.205732763658403,0.81345617746834109}},
{{1.20718795,0.82163474}, {1.15342035,0.527908701}, {1.088,0.296}},
{{1.20568441,0.813443223}, {1.20570859,0.8134497}, {1.20573276,0.813456177}},
{{41.5072916,87.1234036}, {28.2747836,80.9545395}, {23.5780771,69.3344126}},
{{72.9633878,95.6593007}, {42.7738746,88.4730382}, {31.1932785,80.2458029}},
{{31.1663962,54.7302484}, {31.1662882,54.7301074}, {31.1663969,54.7302485}},
{{26.0404936,45.4260361}, {27.7887523,33.1863051}, {40.8833242,26.0301855}},
{{29.9404074,49.1672596}, {44.3131071,45.3915253}, {58.1067559,59.5061814}},
{{72.6510251,64.2972928}, {53.6989659,60.1862397}, {35.2053722,44.8391126}},
{{52.14807018377202, 65.012420045148644}, {44.778669050208237, 66.315562705604378}, {51.619118408823567, 63.787827046262684}},
{{30.004993234763383, 93.921296668202288}, {53.384822003076991, 60.732180341802753}, {58.652998934338584, 43.111073088306185}},
{{80.897794748143198, 49.236332042718459}, {81.082078218891212, 64.066749904488631}, {69.972305057149981, 72.968595519850993}},
{{72.503745601281395, 32.952320736577882}, {88.030880716061645, 38.137194847810164}, {73.193774825517906, 67.773492479591397}},
{{67.426548091427676, 37.993772624988935}, {51.129513170665035, 57.542281234563646}, {44.594748190899189, 65.644267382683879}},
{{61.336508189019057, 82.693132843213675}, {54.825078921449354, 71.663932799212432}, {47.727444217558926, 61.4049645128392}},
{{67.4265481,37.9937726}, {51.1295132,57.5422812}, {44.5947482,65.6442674}},
{{61.3365082,82.6931328}, {54.8250789,71.6639328}, {47.7274442,61.4049645}},
{{53.774852327053594, 53.318060789841951}, {45.787877803416805, 51.393492026284981}, {46.703936967162392, 53.06860709822206}},
{{46.703936967162392, 53.06860709822206}, {47.619996130907957, 54.74372217015916}, {53.020051653535361, 48.633140968832024}},
{{50.934805397717923, 51.52391952648901}, {56.803308902971423, 44.246234610627596}, {69.776888596721406, 40.166645096692555}},
{{50.230212796400401, 38.386469101526998}, {49.855620812184917, 38.818990392153609}, {56.356567496227363, 47.229909093319407}},
{{36.148792695174222, 70.336952793070424}, {36.141613037691357, 70.711654739870085}, {36.154708826402597, 71.088492662905836}},
{{35.216235592661825, 70.580199617313212}, {36.244476835123969, 71.010897787304074}, {37.230244263238326, 71.423156953613102}},
// this pair is nearly coincident, and causes the quartic code to produce bad
// data. Mathematica doesn't think they touch. Graphically, I can't tell.
// it may not be so bad to pretend that they don't touch, if I can detect that
{{369.848602,145.680267}, {382.360413,121.298294}, {406.207703,121.298294}},
{{369.850525,145.675964}, {382.362915,121.29287}, {406.211273,121.29287}},
{{33.567436351153468, 62.336347586395924}, {35.200980274619084, 65.038561460144479}, {36.479571811084995, 67.632178905412445}},
{{41.349524945572696, 67.886658677862641}, {39.125562529359087, 67.429772735149214}, {35.600314083992416, 66.705372160552685}},
{{67.25299631583178, 21.109080184767524}, {43.617595267398613, 33.658034168577529}, {33.38371819435676, 44.214192553988745}},
{{40.476838859398541, 39.543209911285999}, {36.701186108431131, 34.8817994016458}, {30.102144288878023, 26.739063172945315}},
{{25.367434474345036, 50.4712103169743}, {17.865013304933097, 37.356741010559439}, {16.818988838905465, 37.682915484123129}},
{{16.818988838905465, 37.682915484123129}, {15.772964372877833, 38.009089957686811}, {20.624104547604965, 41.825131596683121}},
{{26.440225044088567, 79.695009812848298}, {26.085525979582247, 83.717928354134784}, {27.075079976297072, 84.820633667838905}},
{{27.075079976297072, 84.820633667838905}, {28.276546859574015, 85.988574184029034}, {25.649263209500006, 87.166762066617025}},
{{34.879150914024962, 83.862726601601125}, {35.095810134304429, 83.693473210169543}, {35.359284111931586, 83.488069234177502}},
{{54.503204203015471, 76.094098492518242}, {51.366889541918894, 71.609856061299155}, {46.53086955445437, 69.949863036494207}},
{{0, 0}, {1, 0}, {0, 3}},
{{1, 0}, {0, 1}, {1, 1}},
{{369.961151,137.980698}, {383.970093,121.298294}, {406.213287,121.298294}},
{{353.2948,194.351074}, {353.2948,173.767563}, {364.167572,160.819855}},
{{360.416077,166.795715}, {370.126831,147.872162}, {388.635406,147.872162}},
{{406.236359,121.254936}, {409.445679,121.254936}, {412.975952,121.789818}},
{{406.235992,121.254936}, {425.705902,121.254936}, {439.71994,137.087616}},
{{369.8543701171875, 145.66734313964844}, {382.36788940429688, 121.28203582763672}, {406.21844482421875, 121.28203582763672}},
{{369.96469116210938, 137.96672058105469}, {383.97555541992188, 121.28203582763672}, {406.2218017578125, 121.28203582763672}},
{{369.962311, 137.976044}, {383.971893, 121.29287}, {406.216125, 121.29287}},
{{400.121704, 149.468719}, {391.949493, 161.037186}, {391.949493, 181.202423}},
{{391.946747, 181.839218}, {391.946747, 155.62442}, {406.115479, 138.855438}},
{{360.048828125, 229.2578125}, {360.048828125, 224.4140625}, {362.607421875, 221.3671875}},
{{362.607421875, 221.3671875}, {365.166015625, 218.3203125}, {369.228515625, 218.3203125}},
{{8, 8}, {10, 10}, {8, -10}},
{{8, 8}, {12, 12}, {14, 4}},
{{8, 8}, {9, 9}, {10, 8}}
};
const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
static void oneOffTest1(size_t outer, size_t inner) {
const Quadratic& quad1 = testSet[outer];
const Quadratic& quad2 = testSet[inner];
Intersections intersections2;
intersect2(quad1, quad2, intersections2);
if (intersections2.fUnsortable) {
SkASSERT(0);
return;
}
for (int pt = 0; pt < intersections2.used(); ++pt) {
double tt1 = intersections2.fT[0][pt];
double tx1, ty1;
xy_at_t(quad1, tt1, tx1, ty1);
int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
double tt2 = intersections2.fT[1][pt2];
double tx2, ty2;
xy_at_t(quad2, tt2, tx2, ty2);
if (!AlmostEqualUlps(tx1, tx2)) {
SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
__FUNCTION__, (int)outer, (int)inner, tt1, tx1, ty1, tt2, tx2, ty2);
SkASSERT(0);
}
if (!AlmostEqualUlps(ty1, ty2)) {
SkDebugf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
__FUNCTION__, (int)outer, (int)inner, tt1, tx1, ty1, tt2, tx2, ty2);
SkASSERT(0);
}
#if ONE_OFF_DEBUG
SkDebugf("%s [%d][%d] t1=%1.9g (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
outer, inner, tt1, tx1, ty1, tt2);
#endif
}
}
void QuadraticIntersection_OneOffTest() {
oneOffTest1(0, 1);
oneOffTest1(1, 0);
}
static void oneOffTests() {
for (size_t outer = 0; outer < testSetCount - 1; ++outer) {
for (size_t inner = outer + 1; inner < testSetCount; ++inner) {
oneOffTest1(outer, inner);
}
}
}
static const Quadratic coincidentTestSet[] = {
{{369.850525, 145.675964}, {382.362915, 121.29287}, {406.211273, 121.29287}},
{{369.850525, 145.675964}, {382.362915, 121.29287}, {406.211273, 121.29287}},
{{8, 8}, {10, 10}, {8, -10}},
{{8, -10}, {10, 10}, {8, 8}},
};
const size_t coincidentTestSetCount = sizeof(coincidentTestSet) / sizeof(coincidentTestSet[0]);
static void coincidentTest() {
for (size_t testIndex = 0; testIndex < coincidentTestSetCount - 1; testIndex += 2) {
const Quadratic& quad1 = coincidentTestSet[testIndex];
const Quadratic& quad2 = coincidentTestSet[testIndex + 1];
Intersections intersections2;
intersect2(quad1, quad2, intersections2);
SkASSERT(intersections2.coincidentUsed() == 2);
SkASSERT(intersections2.used() == 2);
for (int pt = 0; pt < intersections2.coincidentUsed(); ++pt) {
SkDEBUGCODE(double tt1 = intersections2.fT[0][pt]);
SkDEBUGCODE(double tt2 = intersections2.fT[1][pt]);
SkASSERT(approximately_equal(1, tt1) || approximately_zero(tt1));
SkASSERT(approximately_equal(1, tt2) || approximately_zero(tt2));
}
}
}
void QuadraticIntersection_Test() {
oneOffTests();
coincidentTest();
standardTestCases();
}
static int floatSign(double x) {
return x < 0 ? -1 : x > 0 ? 1 : 0;
}
static const Quadratic pointFinderTestSet[] = {
//>=0.633974464 0.633974846 <=
{{1.2071879545809394,0.82163474041730045}, {1.1534203513372994,0.52790870069930229}, {1.0880000000000001,0.29599999999999982}}, //t=0.63155333662549329,0.80000000000000004
{{1.2071879545809394,0.82163474041730045}, {1.2065040319428038,0.81766753259119995}, {1.2058123269101506,0.81370135061854221}}, //t=0.63155333662549329,0.6339049773632347
{{1.2058123269101506,0.81370135061854221}, {1.152376363978022,0.5244097415381026}, {1.0880000000000001,0.29599999999999982}}, //t=0.6339049773632347, 0.80000000000000004
//>=0.633974083 0.633975227 <=
{{0.33333333333333326,0.81481481481481488}, {0.63395173631977997,0.68744136726313931}, {1.205684411948591,0.81344322326274499}},//t=0.33333333333333331,0.63395173631977986
{{0.33333333333333326,0.81481481481481488}, {0.63396444791444551,0.68743368362444768}, {1.205732763658403,0.81345617746834109}},//t=0.33333333333333331,0.63396444791444551
{{1.205684411948591,0.81344322326274499}, {1.2057085875611198,0.81344969999329253}, {1.205732763658403,0.81345617746834109}}, //t=0.63395173631977986,0.63396444791444551
{{1.205732763658403,0.81345617746834109}, {1.267928895828891,0.83008534558465619}, {1.3333333333333333,0.85185185185185175}}, //t=0.63396444791444551,0.66666666666666663
};
static void pointFinder(const Quadratic& q1, const Quadratic& q2) {
for (int index = 0; index < 3; ++index) {
double t = nearestT(q1, q2[index]);
_Point onQuad;
xy_at_t(q1, t, onQuad.x, onQuad.y);
SkDebugf("%s t=%1.9g (%1.9g,%1.9g) dist=%1.9g\n", __FUNCTION__, t, onQuad.x, onQuad.y,
onQuad.distance(q2[index]));
double left[3];
left[0] = is_left((const _Line&) q1[0], q2[index]);
left[1] = is_left((const _Line&) q1[1], q2[index]);
_Line diag = {q1[0], q1[2]};
left[2] = is_left(diag, q2[index]);
SkDebugf("%s left=(%d, %d, %d) inHull=%s\n", __FUNCTION__, floatSign(left[0]),
floatSign(left[1]), floatSign(left[2]),
point_in_hull(q1, q2[index]) ? "true" : "false");
}
SkDebugf("\n");
}
static void hullIntersect(const Quadratic& q1, const Quadratic& q2) {
SkDebugf("%s", __FUNCTION__);
Intersections ts;
for (int i1 = 0; i1 < 3; ++i1) {
_Line l1 = {q1[i1], q1[(i1 + 1) % 3]};
for (int i2 = 0; i2 < 3; ++i2) {
_Line l2 = {q2[i2], q2[(i2 + 1) % 3]};
if (intersect(l1, l2, ts)) {
SkDebugf(" [%d,%d]", i1, i2);
}
}
}
SkDebugf("\n");
}
void QuadraticIntersection_PointFinder() {
pointFinder(pointFinderTestSet[0], pointFinderTestSet[4]);
pointFinder(pointFinderTestSet[4], pointFinderTestSet[0]);
pointFinder(pointFinderTestSet[0], pointFinderTestSet[6]);
pointFinder(pointFinderTestSet[6], pointFinderTestSet[0]);
hullIntersect(pointFinderTestSet[0], pointFinderTestSet[4]);
hullIntersect(pointFinderTestSet[0], pointFinderTestSet[6]);
}
static void intersectionFinder(int test1, int test2) {
const Quadratic& quad1 = testSet[test1];
const Quadratic& quad2 = testSet[test2];
double t1Seed = 0.5;
double t2Seed = 0.8;
double t1Step = 0.1;
double t2Step = 0.1;
_Point t1[3], t2[3];
bool toggle = true;
do {
xy_at_t(quad1, t1Seed - t1Step, t1[0].x, t1[0].y);
xy_at_t(quad1, t1Seed, t1[1].x, t1[1].y);
xy_at_t(quad1, t1Seed + t1Step, t1[2].x, t1[2].y);
xy_at_t(quad2, t2Seed - t2Step, t2[0].x, t2[0].y);
xy_at_t(quad2, t2Seed, t2[1].x, t2[1].y);
xy_at_t(quad2, t2Seed + t2Step, t2[2].x, t2[2].y);
double dist[3][3];
dist[1][1] = t1[1].distance(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].distance(t2[j]);
if (dist[best_i][best_j] > dist[i][j]) {
best_i = i;
best_j = j;
}
}
}
if (best_i == 0) {
t1Seed -= t1Step;
} else if (best_i == 2) {
t1Seed += t1Step;
}
if (best_j == 0) {
t2Seed -= t2Step;
} else if (best_j == 2) {
t2Seed += t2Step;
}
if (best_i == 1 && best_j == 1) {
if ((toggle ^= true)) {
t1Step /= 2;
} else {
t2Step /= 2;
}
}
} while (!t1[1].approximatelyEqual(t2[1]));
t1Step = t2Step = 0.1;
double t10 = t1Seed - t1Step * 2;
double t12 = t1Seed + t1Step * 2;
double t20 = t2Seed - t2Step * 2;
double t22 = t2Seed + t2Step * 2;
_Point test;
while (!approximately_zero(t1Step)) {
xy_at_t(quad1, t10, test.x, test.y);
t10 += t1[1].approximatelyEqual(test) ? -t1Step : t1Step;
t1Step /= 2;
}
t1Step = 0.1;
while (!approximately_zero(t1Step)) {
xy_at_t(quad1, t12, test.x, test.y);
t12 -= t1[1].approximatelyEqual(test) ? -t1Step : t1Step;
t1Step /= 2;
}
while (!approximately_zero(t2Step)) {
xy_at_t(quad2, t20, test.x, test.y);
t20 += t2[1].approximatelyEqual(test) ? -t2Step : t2Step;
t2Step /= 2;
}
t2Step = 0.1;
while (!approximately_zero(t2Step)) {
xy_at_t(quad2, t22, test.x, test.y);
t22 -= t2[1].approximatelyEqual(test) ? -t2Step : t2Step;
t2Step /= 2;
}
#if ONE_OFF_DEBUG
SkDebugf("%s t1=(%1.9g<%1.9g<%1.9g) t2=(%1.9g<%1.9g<%1.9g)\n", __FUNCTION__,
t10, t1Seed, t12, t20, t2Seed, t22);
_Point p10 = xy_at_t(quad1, t10);
_Point p1Seed = xy_at_t(quad1, t1Seed);
_Point p12 = xy_at_t(quad1, t12);
SkDebugf("%s p1=(%1.9g,%1.9g)<(%1.9g,%1.9g)<(%1.9g,%1.9g)\n", __FUNCTION__,
p10.x, p10.y, p1Seed.x, p1Seed.y, p12.x, p12.y);
_Point p20 = xy_at_t(quad2, t20);
_Point p2Seed = xy_at_t(quad2, t2Seed);
_Point p22 = xy_at_t(quad2, t22);
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);
#endif
}
void QuadraticIntersection_IntersectionFinder() {
intersectionFinder(0, 1);
}

View File

@ -1,98 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "QuadraticIntersection_TestData.h"
const Quadratic quadraticLines[] = {
{{0, 0}, {0, 0}, {1, 0}},
{{0, 0}, {1, 0}, {0, 0}},
{{1, 0}, {0, 0}, {0, 0}},
{{1, 0}, {2, 0}, {3, 0}},
{{0, 0}, {0, 0}, {0, 1}},
{{0, 0}, {0, 1}, {0, 0}},
{{0, 1}, {0, 0}, {0, 0}},
{{0, 1}, {0, 2}, {0, 3}},
{{0, 0}, {0, 0}, {1, 1}},
{{0, 0}, {1, 1}, {0, 0}},
{{1, 1}, {0, 0}, {0, 0}},
{{1, 1}, {2, 2}, {3, 3}},
{{1, 1}, {3, 3}, {3, 3}},
{{1, 1}, {1, 1}, {2, 2}},
{{1, 1}, {2, 2}, {1, 1}},
{{1, 1}, {1, 1}, {3, 3}},
{{1, 1}, {2, 2}, {4, 4}}, // no coincident
{{1, 1}, {3, 3}, {4, 4}},
{{1, 1}, {3, 3}, {2, 2}},
{{1, 1}, {4, 4}, {2, 2}},
{{1, 1}, {4, 4}, {3, 3}},
{{2, 2}, {1, 1}, {3, 3}},
{{2, 2}, {1, 1}, {4, 4}},
{{2, 2}, {3, 3}, {1, 1}},
{{2, 2}, {3, 3}, {4, 4}},
{{2, 2}, {4, 4}, {1, 1}},
{{2, 2}, {4, 4}, {3, 3}},
};
const size_t quadraticLines_count = sizeof(quadraticLines) / sizeof(quadraticLines[0]);
static const double F = PointEpsilon * 3;
static const double H = PointEpsilon * 4;
static const double J = PointEpsilon * 5;
static const double K = PointEpsilon * 8; // INVESTIGATE: why are larger multiples necessary?
const Quadratic quadraticModEpsilonLines[] = {
{{0, F}, {0, 0}, {1, 0}},
{{0, 0}, {1, 0}, {0, F}},
{{1, 0}, {0, F}, {0, 0}},
{{1, H}, {2, 0}, {3, 0}},
{{F, 0}, {0, 0}, {0, 1}},
{{0, 0}, {0, 1}, {F, 0}},
{{0, 1}, {F, 0}, {0, 0}},
{{H, 1}, {0, 2}, {0, 3}},
{{0, F}, {0, 0}, {1, 1}},
{{0, 0}, {1, 1}, {F, 0}},
{{1, 1}, {F, 0}, {0, 0}},
{{1, 1+J}, {2, 2}, {3, 3}},
{{1, 1}, {3, 3}, {3+F, 3}},
{{1, 1}, {1+F, 1}, {2, 2}},
{{1, 1}, {2, 2}, {1, 1+F}},
{{1, 1}, {1, 1+F}, {3, 3}},
{{1+H, 1}, {2, 2}, {4, 4}}, // no coincident
{{1, 1+K}, {3, 3}, {4, 4}},
{{1, 1}, {3+F, 3}, {2, 2}},
{{1, 1}, {4, 4+F}, {2, 2}},
{{1, 1}, {4, 4}, {3+F, 3}},
{{2, 2}, {1, 1}, {3, 3+F}},
{{2+F, 2}, {1, 1}, {4, 4}},
{{2, 2+F}, {3, 3}, {1, 1}},
{{2, 2}, {3+F, 3}, {4, 4}},
{{2, 2}, {4, 4+F}, {1, 1}},
{{2, 2}, {4, 4}, {3+F, 3}},
};
const size_t quadraticModEpsilonLines_count = sizeof(quadraticModEpsilonLines) / sizeof(quadraticModEpsilonLines[0]);
const Quadratic quadraticTests[][2] = {
{ // one intersection
{{0, 0},
{0, 1},
{1, 1}},
{{0, 1},
{0, 0},
{1, 0}}
},
{ // four intersections
{{1, 0},
{2, 6},
{3, 0}},
{{0, 1},
{6, 2},
{0, 3}}
}
};
const size_t quadraticTests_count = sizeof(quadraticTests) / sizeof(quadraticTests[0]);

View File

@ -1,16 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
#include "DataTypes_Test.h"
extern const Quadratic quadraticLines[];
extern const Quadratic quadraticModEpsilonLines[];
extern const Quadratic quadraticTests[][2];
extern const size_t quadraticLines_count;
extern const size_t quadraticModEpsilonLines_count;
extern const size_t quadraticTests_count;

View File

@ -1,34 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "QuadraticLineSegments.h"
// http://cagd.cs.byu.edu/~557/text/cagd.pdf 2.7
// A hodograph is the first derivative curve
void hodograph(const Quadratic& quad, _Line& hodo) {
hodo[0].x = 2 * (quad[1].x - quad[0].x);
hodo[0].y = 2 * (quad[1].y - quad[0].y);
hodo[1].x = 2 * (quad[2].x - quad[1].x);
hodo[1].y = 2 * (quad[2].y - quad[1].y);
}
// A 2nd hodograph is the second derivative curve
void secondHodograph(const Quadratic& quad, _Point& hodo2) {
_Line hodo;
hodograph(quad, hodo);
hodo2.x = hodo[1].x - hodo[0].x;
hodo2.y = hodo[1].y - hodo[0].y;
}
// The number of line segments required to approximate the quad
// see http://cagd.cs.byu.edu/~557/text/cagd.pdf 10.6
double subDivisions(const Quadratic& quad) {
_Point hodo2;
secondHodograph(quad, hodo2);
double dist = sqrt(hodo2.x * hodo2.x + hodo2.y * hodo2.y);
double segments = sqrt(dist / (8 * FLT_EPSILON));
return segments;
}

View File

@ -1,12 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DataTypes.h"
void hodograph(const Quadratic& , _Line& hodo);
void secondHodograph(const Quadratic& , _Point& hodo2);
double subDivisions(const Quadratic& );

View File

@ -1,135 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "QuadraticParameterization.h"
#include "QuadraticUtilities.h"
/* from http://tom.cs.byu.edu/~tom/papers/cvgip84.pdf 4.1
*
* This paper proves that Syvester's method can compute the implicit form of
* the quadratic from the parameterized form.
*
* Given x = a*t*t + b*t + c (the parameterized form)
* y = d*t*t + e*t + f
*
* we want to find an equation of the implicit form:
*
* A*x*x + B*x*y + C*y*y + D*x + E*y + F = 0
*
* The implicit form can be expressed as a 4x4 determinant, as shown.
*
* The resultant obtained by Syvester's method is
*
* | a b (c - x) 0 |
* | 0 a b (c - x) |
* | d e (f - y) 0 |
* | 0 d e (f - y) |
*
* which expands to
*
* d*d*x*x + -2*a*d*x*y + a*a*y*y
* + (-2*c*d*d + b*e*d - a*e*e + 2*a*f*d)*x
* + (-2*f*a*a + e*b*a - d*b*b + 2*d*c*a)*y
* +
* | a b c 0 |
* | 0 a b c | == 0.
* | d e f 0 |
* | 0 d e f |
*
* Expanding the constant determinant results in
*
* | a b c | | b c 0 |
* a*| e f 0 | + d*| a b c | ==
* | d e f | | d e f |
*
* a*(a*f*f + c*e*e - c*f*d - b*e*f) + d*(b*b*f + c*c*d - c*a*f - c*e*b)
*
*/
static bool straight_forward = true;
QuadImplicitForm::QuadImplicitForm(const Quadratic& q) {
double a, b, c;
set_abc(&q[0].x, a, b, c);
double d, e, f;
set_abc(&q[0].y, d, e, f);
// compute the implicit coefficients
if (straight_forward) { // 42 muls, 13 adds
p[xx_coeff] = d * d;
p[xy_coeff] = -2 * a * d;
p[yy_coeff] = a * a;
p[x_coeff] = -2*c*d*d + b*e*d - a*e*e + 2*a*f*d;
p[y_coeff] = -2*f*a*a + e*b*a - d*b*b + 2*d*c*a;
p[c_coeff] = a*(a*f*f + c*e*e - c*f*d - b*e*f)
+ d*(b*b*f + c*c*d - c*a*f - c*e*b);
} else { // 26 muls, 11 adds
double aa = a * a;
double ad = a * d;
double dd = d * d;
p[xx_coeff] = dd;
p[xy_coeff] = -2 * ad;
p[yy_coeff] = aa;
double be = b * e;
double bde = be * d;
double cdd = c * dd;
double ee = e * e;
p[x_coeff] = -2*cdd + bde - a*ee + 2*ad*f;
double aaf = aa * f;
double abe = a * be;
double ac = a * c;
double bb_2ac = b*b - 2*ac;
p[y_coeff] = -2*aaf + abe - d*bb_2ac;
p[c_coeff] = aaf*f + ac*ee + d*f*bb_2ac - abe*f + c*cdd - c*bde;
}
}
/* Given a pair of quadratics, determine their parametric coefficients.
* If the scaled coefficients are nearly equal, then the part of the quadratics
* may be coincident.
* FIXME: optimization -- since comparison short-circuits on no match,
* lazily compute the coefficients, comparing the easiest to compute first.
* xx and yy first; then xy; and so on.
*/
bool QuadImplicitForm::implicit_match(const QuadImplicitForm& p2) const {
int first = 0;
for (int index = 0; index < coeff_count; ++index) {
if (approximately_zero(p[index]) && approximately_zero(p2.p[index])) {
first += first == index;
continue;
}
if (first == index) {
continue;
}
if (!AlmostEqualUlps(p[index] * p2.p[first], p[first] * p2.p[index])) {
return false;
}
}
return true;
}
bool implicit_matches(const Quadratic& quad1, const Quadratic& quad2) {
QuadImplicitForm i1(quad1); // a'xx , b'xy , c'yy , d'x , e'y , f
QuadImplicitForm i2(quad2);
return i1.implicit_match(i2);
}
static double tangent(const double* quadratic, double t) {
double a, b, c;
set_abc(quadratic, a, b, c);
return 2 * a * t + b;
}
void tangent(const Quadratic& quadratic, double t, _Point& result) {
result.x = tangent(&quadratic[0].x, t);
result.y = tangent(&quadratic[0].y, t);
}
// unit test to return and validate parametric coefficients
#include "QuadraticParameterization_TestUtility.cpp"

View File

@ -1,27 +0,0 @@
#include "DataTypes.h"
class QuadImplicitForm {
public:
QuadImplicitForm(const Quadratic& q);
bool implicit_match(const QuadImplicitForm& two) const;
double x2() const { return p[xx_coeff]; }
double xy() const { return p[xy_coeff]; }
double y2() const { return p[yy_coeff]; }
double x() const { return p[x_coeff]; }
double y() const { return p[y_coeff]; }
double c() const { return p[c_coeff]; }
private:
enum Coeffs {
xx_coeff,
xy_coeff,
yy_coeff,
x_coeff,
y_coeff,
c_coeff,
coeff_count
};
double p[coeff_count];
};

View File

@ -1,48 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Intersection_Tests.h"
#include "Parameterization_Test.h"
#include "QuadraticUtilities.h"
const Quadratic quadratics[] = {
{{0, 0}, {1, 0}, {1, 1}},
};
const size_t quadratics_count = sizeof(quadratics) / sizeof(quadratics[0]);
int firstQuadraticCoincidenceTest = 0;
void QuadraticCoincidence_Test() {
// split large quadratic
// compare original, parts, to see if the are coincident
for (size_t index = firstQuadraticCoincidenceTest; index < quadratics_count; ++index) {
const Quadratic& test = quadratics[index];
QuadraticPair split;
chop_at(test, split, 0.5);
Quadratic midThird;
sub_divide(test, 1.0/3, 2.0/3, midThird);
const Quadratic* quads[] = {
&test, &midThird, &split.first(), &split.second()
};
size_t quadsCount = sizeof(quads) / sizeof(quads[0]);
for (size_t one = 0; one < quadsCount; ++one) {
for (size_t two = 0; two < quadsCount; ++two) {
for (size_t inner = 0; inner < 3; inner += 2) {
if (!point_on_parameterized_curve(*quads[one], (*quads[two])[inner])) {
SkDebugf("%s %zu [%zu,%zu] %zu parameterization failed\n",
__FUNCTION__, index, one, two, inner);
}
}
if (!implicit_matches(*quads[one], *quads[two])) {
SkDebugf("%s %zu [%zu,%zu] coincidence failed\n", __FUNCTION__,
index, one, two);
}
}
}
}
}

View File

@ -1,16 +0,0 @@
// included by QuadraticParameterization.cpp
// accesses internal functions to validate parameterized coefficients
#include "Parameterization_Test.h"
bool point_on_parameterized_curve(const Quadratic& quad, const _Point& point) {
QuadImplicitForm q(quad);
double xx = q.x2() * point.x * point.x;
double xy = q.xy() * point.x * point.y;
double yy = q.y2() * point.y * point.y;
double x = q.x() * point.x;
double y = q.y() * point.y;
double c = q.c();
double sum = xx + xy + yy + x + y + c;
return approximately_zero(sum);
}

View File

@ -1,184 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Extrema.h"
#include "IntersectionUtilities.h"
#include "LineParameters.h"
static double interp_quad_coords(double a, double b, double c, double t)
{
double ab = interp(a, b, t);
double bc = interp(b, c, t);
return interp(ab, bc, t);
}
static int coincident_line(const Quadratic& quad, Quadratic& reduction) {
reduction[0] = reduction[1] = quad[0];
return 1;
}
static int vertical_line(const Quadratic& quad, ReduceOrder_Styles reduceStyle,
Quadratic& reduction) {
double tValue;
reduction[0] = quad[0];
reduction[1] = quad[2];
if (reduceStyle == kReduceOrder_TreatAsFill) {
return 2;
}
int smaller = reduction[1].y > reduction[0].y;
int larger = smaller ^ 1;
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;
} else if (reduction[larger].y < yExtrema) {
reduction[larger].y = yExtrema;
}
}
return 2;
}
static int horizontal_line(const Quadratic& quad, ReduceOrder_Styles reduceStyle,
Quadratic& reduction) {
double tValue;
reduction[0] = quad[0];
reduction[1] = quad[2];
if (reduceStyle == kReduceOrder_TreatAsFill) {
return 2;
}
int smaller = reduction[1].x > reduction[0].x;
int larger = smaller ^ 1;
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;
} else if (reduction[larger].x < xExtrema) {
reduction[larger].x = xExtrema;
}
}
return 2;
}
static int check_linear(const Quadratic& quad, ReduceOrder_Styles reduceStyle,
int minX, int maxX, int minY, int maxY, Quadratic& reduction) {
int startIndex = 0;
int endIndex = 2;
while (quad[startIndex].approximatelyEqual(quad[endIndex])) {
--endIndex;
if (endIndex == 0) {
printf("%s shouldn't get here if all four points are about equal", __FUNCTION__);
SkASSERT(0);
}
}
if (!isLinear(quad, startIndex, endIndex)) {
return 0;
}
// four are colinear: return line formed by outside
reduction[0] = quad[0];
reduction[1] = quad[2];
if (reduceStyle == kReduceOrder_TreatAsFill) {
return 2;
}
int sameSide;
bool useX = quad[maxX].x - quad[minX].x >= quad[maxY].y - quad[minY].y;
if (useX) {
sameSide = sign(quad[0].x - quad[1].x) + sign(quad[2].x - quad[1].x);
} else {
sameSide = sign(quad[0].y - quad[1].y) + sign(quad[2].y - quad[1].y);
}
if ((sameSide & 3) != 2) {
return 2;
}
double tValue;
int root;
if (useX) {
root = findExtrema(quad[0].x, quad[1].x, quad[2].x, &tValue);
} else {
root = findExtrema(quad[0].y, quad[1].y, quad[2].y, &tValue);
}
if (root) {
_Point extrema;
extrema.x = interp_quad_coords(quad[0].x, quad[1].x, quad[2].x, tValue);
extrema.y = interp_quad_coords(quad[0].y, quad[1].y, quad[2].y, tValue);
// sameSide > 0 means mid is smaller than either [0] or [2], so replace smaller
int replace;
if (useX) {
if (extrema.x < quad[0].x ^ extrema.x < quad[2].x) {
return 2;
}
replace = (extrema.x < quad[0].x | extrema.x < quad[2].x)
^ (quad[0].x < quad[2].x);
} else {
if (extrema.y < quad[0].y ^ extrema.y < quad[2].y) {
return 2;
}
replace = (extrema.y < quad[0].y | extrema.y < quad[2].y)
^ (quad[0].y < quad[2].y);
}
reduction[replace] = extrema;
}
return 2;
}
bool isLinear(const Quadratic& quad, int startIndex, int endIndex) {
LineParameters lineParameters;
lineParameters.quadEndPoints(quad, startIndex, endIndex);
// FIXME: maybe it's possible to avoid this and compare non-normalized
lineParameters.normalize();
double distance = lineParameters.controlPtDistance(quad);
return approximately_zero(distance);
}
// reduce to a quadratic or smaller
// look for identical points
// look for all four points in a line
// note that three points in a line doesn't simplify a cubic
// look for approximation with single quadratic
// save approximation with multiple quadratics for later
int reduceOrder(const Quadratic& quad, Quadratic& reduction, ReduceOrder_Styles reduceStyle) {
int index, minX, maxX, minY, maxY;
int minXSet, minYSet;
minX = maxX = minY = maxY = 0;
minXSet = minYSet = 0;
for (index = 1; index < 3; ++index) {
if (quad[minX].x > quad[index].x) {
minX = index;
}
if (quad[minY].y > quad[index].y) {
minY = index;
}
if (quad[maxX].x < quad[index].x) {
maxX = index;
}
if (quad[maxY].y < quad[index].y) {
maxY = index;
}
}
for (index = 0; index < 3; ++index) {
if (AlmostEqualUlps(quad[index].x, quad[minX].x)) {
minXSet |= 1 << index;
}
if (AlmostEqualUlps(quad[index].y, quad[minY].y)) {
minYSet |= 1 << index;
}
}
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, reduceStyle, reduction);
}
if (minYSet == 0xF) { // test for horizontal line
return horizontal_line(quad, reduceStyle, reduction);
}
int result = check_linear(quad, reduceStyle, minX, maxX, minY, maxY, reduction);
if (result) {
return result;
}
memcpy(reduction, quad, sizeof(Quadratic));
return 3;
}

View File

@ -1,67 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Intersection_Tests.h"
#include "QuadraticIntersection_TestData.h"
#include "TestUtilities.h"
static const Quadratic testSet[] = {
{{1, 1}, {2, 2}, {1, 1.000003}},
{{1, 0}, {2, 6}, {3, 0}}
};
static const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
static void oneOffTest() {
SkDebugf("%s FLT_EPSILON=%1.9g\n", __FUNCTION__, FLT_EPSILON);
for (size_t index = 0; index < testSetCount; ++index) {
const Quadratic& quad = testSet[index];
Quadratic reduce;
SkDEBUGCODE(int result = ) reduceOrder(quad, reduce, kReduceOrder_TreatAsFill);
SkASSERT(result == 3);
}
}
static void standardTestCases() {
size_t index;
Quadratic reduce;
int order;
enum {
RunAll,
RunQuadraticLines,
RunQuadraticModLines,
RunNone
} run = RunAll;
int firstTestIndex = 0;
#if 0
run = RunQuadraticLines;
firstTestIndex = 1;
#endif
int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : SK_MaxS32;
int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : SK_MaxS32;
for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
const Quadratic& quad = quadraticLines[index];
order = reduceOrder(quad, reduce, kReduceOrder_TreatAsFill);
if (order != 2) {
printf("[%d] line quad order=%d\n", (int) index, order);
}
}
for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
const Quadratic& quad = quadraticModEpsilonLines[index];
order = reduceOrder(quad, reduce, kReduceOrder_TreatAsFill);
if (order != 3) {
printf("[%d] line mod quad order=%d\n", (int) index, order);
}
}
}
void QuadraticReduceOrder_Test() {
oneOffTest();
standardTestCases();
}

View File

@ -1,79 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "IntersectionUtilities.h"
#include "QuadraticUtilities.h"
/*
Given a quadratic q, t1, and t2, find a small quadratic segment.
The new quadratic is defined by A, B, and C, where
A = c[0]*(1 - t1)*(1 - t1) + 2*c[1]*t1*(1 - t1) + c[2]*t1*t1
C = c[3]*(1 - t1)*(1 - t1) + 2*c[2]*t1*(1 - t1) + c[1]*t1*t1
To find B, compute the point halfway between t1 and t2:
q(at (t1 + t2)/2) == D
Next, compute where D must be if we know the value of B:
_12 = A/2 + B/2
12_ = B/2 + C/2
123 = A/4 + B/2 + C/4
= D
Group the known values on one side:
B = D*2 - A/2 - C/2
*/
static double interp_quad_coords(const double* src, double t)
{
double ab = interp(src[0], src[2], t);
double bc = interp(src[2], src[4], t);
double abc = interp(ab, bc, t);
return abc;
}
void sub_divide(const Quadratic& src, double t1, double t2, Quadratic& dst) {
double ax = dst[0].x = interp_quad_coords(&src[0].x, t1);
double ay = dst[0].y = interp_quad_coords(&src[0].y, t1);
double dx = interp_quad_coords(&src[0].x, (t1 + t2) / 2);
double dy = interp_quad_coords(&src[0].y, (t1 + t2) / 2);
double cx = dst[2].x = interp_quad_coords(&src[0].x, t2);
double cy = dst[2].y = interp_quad_coords(&src[0].y, t2);
/* bx = */ dst[1].x = 2*dx - (ax + cx)/2;
/* by = */ dst[1].y = 2*dy - (ay + cy)/2;
}
_Point sub_divide(const Quadratic& src, const _Point& a, const _Point& c, double t1, double t2) {
_Point b;
double dx = interp_quad_coords(&src[0].x, (t1 + t2) / 2);
double dy = interp_quad_coords(&src[0].y, (t1 + t2) / 2);
b.x = 2 * dx - (a.x + c.x) / 2;
b.y = 2 * dy - (a.y + c.y) / 2;
return b;
}
/* classic one t subdivision */
static void interp_quad_coords(const double* src, double* dst, double t)
{
double ab = interp(src[0], src[2], t);
double bc = interp(src[2], src[4], t);
dst[0] = src[0];
dst[2] = ab;
dst[4] = interp(ab, bc, t);
dst[6] = bc;
dst[8] = src[4];
}
void chop_at(const Quadratic& src, QuadraticPair& dst, double t)
{
interp_quad_coords(&src[0].x, &dst.pts[0].x, t);
interp_quad_coords(&src[0].y, &dst.pts[0].y, t);
}

View File

@ -1,255 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "CubicUtilities.h"
#include "Extrema.h"
#include "QuadraticUtilities.h"
#include "TriangleUtilities.h"
// from http://blog.gludion.com/2009/08/distance-to-quadratic-bezier-curve.html
double nearestT(const Quadratic& quad, const _Point& pt) {
_Vector pos = quad[0] - pt;
// search points P of bezier curve with PM.(dP / dt) = 0
// a calculus leads to a 3d degree equation :
_Vector A = quad[1] - quad[0];
_Vector B = quad[2] - quad[1];
B -= A;
double a = B.dot(B);
double b = 3 * A.dot(B);
double c = 2 * A.dot(A) + pos.dot(B);
double d = pos.dot(A);
double ts[3];
int roots = cubicRootsValidT(a, b, c, d, ts);
double d0 = pt.distanceSquared(quad[0]);
double d2 = pt.distanceSquared(quad[2]);
double distMin = SkTMin(d0, d2);
int bestIndex = -1;
for (int index = 0; index < roots; ++index) {
_Point onQuad;
xy_at_t(quad, ts[index], onQuad.x, onQuad.y);
double dist = pt.distanceSquared(onQuad);
if (distMin > dist) {
distMin = dist;
bestIndex = index;
}
}
if (bestIndex >= 0) {
return ts[bestIndex];
}
return d0 < d2 ? 0 : 1;
}
bool point_in_hull(const Quadratic& quad, const _Point& pt) {
return pointInTriangle((const Triangle&) quad, pt);
}
_Point top(const Quadratic& quad, double startT, double endT) {
Quadratic sub;
sub_divide(quad, startT, endT, sub);
_Point topPt = sub[0];
if (topPt.y > sub[2].y || (topPt.y == sub[2].y && topPt.x > sub[2].x)) {
topPt = sub[2];
}
if (!between(sub[0].y, sub[1].y, sub[2].y)) {
double extremeT;
if (findExtrema(sub[0].y, sub[1].y, sub[2].y, &extremeT)) {
extremeT = startT + (endT - startT) * extremeT;
_Point test;
xy_at_t(quad, extremeT, test.x, test.y);
if (topPt.y > test.y || (topPt.y == test.y && topPt.x > test.x)) {
topPt = test;
}
}
}
return topPt;
}
/*
Numeric Solutions (5.6) suggests to solve the quadratic by computing
Q = -1/2(B + sgn(B)Sqrt(B^2 - 4 A C))
and using the roots
t1 = Q / A
t2 = C / Q
*/
int add_valid_ts(double s[], int realRoots, double* t) {
int foundRoots = 0;
for (int index = 0; index < realRoots; ++index) {
double tValue = s[index];
if (approximately_zero_or_more(tValue) && approximately_one_or_less(tValue)) {
if (approximately_less_than_zero(tValue)) {
tValue = 0;
} else if (approximately_greater_than_one(tValue)) {
tValue = 1;
}
for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
if (approximately_equal(t[idx2], tValue)) {
goto nextRoot;
}
}
t[foundRoots++] = tValue;
}
nextRoot:
;
}
return foundRoots;
}
// note: caller expects multiple results to be sorted smaller first
// note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting
// analysis of the quadratic equation, suggesting why the following looks at
// the sign of B -- and further suggesting that the greatest loss of precision
// is in b squared less two a c
int quadraticRootsValidT(double A, double B, double C, double t[2]) {
#if 0
B *= 2;
double square = B * B - 4 * A * C;
if (approximately_negative(square)) {
if (!approximately_positive(square)) {
return 0;
}
square = 0;
}
double squareRt = sqrt(square);
double Q = (B + (B < 0 ? -squareRt : squareRt)) / -2;
int foundRoots = 0;
double ratio = Q / A;
if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) {
if (approximately_less_than_zero(ratio)) {
ratio = 0;
} else if (approximately_greater_than_one(ratio)) {
ratio = 1;
}
t[0] = ratio;
++foundRoots;
}
ratio = C / Q;
if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) {
if (approximately_less_than_zero(ratio)) {
ratio = 0;
} else if (approximately_greater_than_one(ratio)) {
ratio = 1;
}
if (foundRoots == 0 || !approximately_negative(ratio - t[0])) {
t[foundRoots++] = ratio;
} else if (!approximately_negative(t[0] - ratio)) {
t[foundRoots++] = t[0];
t[0] = ratio;
}
}
#else
double s[2];
int realRoots = quadraticRootsReal(A, B, C, s);
int foundRoots = add_valid_ts(s, realRoots, t);
#endif
return foundRoots;
}
// unlike quadratic roots, this does not discard real roots <= 0 or >= 1
int quadraticRootsReal(const double A, const double B, const double C, double s[2]) {
const double p = B / (2 * A);
const double q = C / A;
if (approximately_zero(A) && (approximately_zero_inverse(p) || approximately_zero_inverse(q))) {
if (approximately_zero(B)) {
s[0] = 0;
return C == 0;
}
s[0] = -C / B;
return 1;
}
/* normal form: x^2 + px + q = 0 */
const double p2 = p * p;
#if 0
double D = AlmostEqualUlps(p2, q) ? 0 : p2 - q;
if (D <= 0) {
if (D < 0) {
return 0;
}
s[0] = -p;
SkDebugf("[%d] %1.9g\n", 1, s[0]);
return 1;
}
double sqrt_D = sqrt(D);
s[0] = sqrt_D - p;
s[1] = -sqrt_D - p;
SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]);
return 2;
#else
if (!AlmostEqualUlps(p2, q) && p2 < q) {
return 0;
}
double sqrt_D = 0;
if (p2 > q) {
sqrt_D = sqrt(p2 - q);
}
s[0] = sqrt_D - p;
s[1] = -sqrt_D - p;
#if 0
if (AlmostEqualUlps(s[0], s[1])) {
SkDebugf("[%d] %1.9g\n", 1, s[0]);
} else {
SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]);
}
#endif
return 1 + !AlmostEqualUlps(s[0], s[1]);
#endif
}
void toCubic(const Quadratic& quad, Cubic& cubic) {
cubic[0] = quad[0];
cubic[2] = quad[1];
cubic[3] = quad[2];
cubic[1].x = (cubic[0].x + cubic[2].x * 2) / 3;
cubic[1].y = (cubic[0].y + cubic[2].y * 2) / 3;
cubic[2].x = (cubic[3].x + cubic[2].x * 2) / 3;
cubic[2].y = (cubic[3].y + cubic[2].y * 2) / 3;
}
static double derivativeAtT(const double* quad, double t) {
double a = t - 1;
double b = 1 - 2 * t;
double c = t;
return a * quad[0] + b * quad[2] + c * quad[4];
}
double dx_at_t(const Quadratic& quad, double t) {
return derivativeAtT(&quad[0].x, t);
}
double dy_at_t(const Quadratic& quad, double t) {
return derivativeAtT(&quad[0].y, t);
}
_Vector dxdy_at_t(const Quadratic& quad, double t) {
double a = t - 1;
double b = 1 - 2 * t;
double c = t;
_Vector result = { a * quad[0].x + b * quad[1].x + c * quad[2].x,
a * quad[0].y + b * quad[1].y + c * quad[2].y };
return result;
}
void xy_at_t(const Quadratic& quad, double t, double& x, double& y) {
double one_t = 1 - t;
double a = one_t * one_t;
double b = 2 * one_t * t;
double c = t * t;
if (&x) {
x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
}
if (&y) {
y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
}
}
_Point xy_at_t(const Quadratic& quad, double t) {
double one_t = 1 - t;
double a = one_t * one_t;
double b = 2 * one_t * t;
double c = t * t;
_Point result = { a * quad[0].x + b * quad[1].x + c * quad[2].x,
a * quad[0].y + b * quad[1].y + c * quad[2].y };
return result;
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#if !defined QUADRATIC_UTILITIES_H
#define QUADRATIC_UTILITIES_H
#include "DataTypes.h"
int add_valid_ts(double s[], int realRoots, double* t);
void chop_at(const Quadratic& src, QuadraticPair& dst, double t);
double dx_at_t(const Quadratic& , double t);
double dy_at_t(const Quadratic& , double t);
//void dxdy_at_t(const Quadratic& , double t, _Point& xy);
_Vector dxdy_at_t(const Quadratic& , double t);
double nearestT(const Quadratic& , const _Point& );
bool point_in_hull(const Quadratic& , const _Point& );
/* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
*
* a = A - 2*B + C
* b = 2*B - 2*C
* c = C
*/
inline void set_abc(const double* quad, double& a, double& b, double& c) {
a = quad[0]; // a = A
b = 2 * quad[2]; // b = 2*B
c = quad[4]; // c = C
b -= c; // b = 2*B - C
a -= b; // a = A - 2*B + C
b -= c; // b = 2*B - 2*C
}
int quadraticRootsReal(double A, double B, double C, double t[2]);
int quadraticRootsValidT(const double A, const double B, const double C, double s[2]);
void sub_divide(const Quadratic& src, double t1, double t2, Quadratic& dst);
_Point sub_divide(const Quadratic& src, const _Point& a, const _Point& c, double t1, double t2);
void toCubic(const Quadratic& , Cubic& );
_Point top(const Quadratic& , double startT, double endT);
void xy_at_t(const Quadratic& , double t, double& x, double& y);
_Point xy_at_t(const Quadratic& , double t);
#endif

View File

@ -1,236 +0,0 @@
// from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
/*
* Roots3And4.c
*
* Utility functions to find cubic and quartic roots,
* coefficients are passed like this:
*
* c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
*
* The functions return the number of non-complex roots and
* put the values into the s array.
*
* Author: Jochen Schwarze (schwarze@isa.de)
*
* Jan 26, 1990 Version for Graphics Gems
* Oct 11, 1990 Fixed sign problem for negative q's in SolveQuartic
* (reported by Mark Podlipec),
* Old-style function definitions,
* IsZero() as a macro
* Nov 23, 1990 Some systems do not declare acos() and cbrt() in
* <math.h>, though the functions exist in the library.
* If large coefficients are used, EQN_EPS should be
* reduced considerably (e.g. to 1E-30), results will be
* correct but multiple roots might be reported more
* than once.
*/
#include <math.h>
#include "CubicUtilities.h"
#include "QuadraticUtilities.h"
#include "QuarticRoot.h"
int reducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
const double t0, const bool oneHint, double roots[4]) {
#ifdef SK_DEBUG
// create a string mathematica understands
// GDB set print repe 15 # if repeated digits is a bother
// set print elements 400 # if line doesn't fit
char str[1024];
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]",
t4, t3, t2, t1, t0);
mathematica_ize(str, sizeof(str));
#if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
SkDebugf("%s\n", str);
#endif
#endif
#if 0 && SK_DEBUG
bool t4Or = approximately_zero_when_compared_to(t4, t0) // 0 is one root
|| approximately_zero_when_compared_to(t4, t1)
|| approximately_zero_when_compared_to(t4, t2);
bool t4And = approximately_zero_when_compared_to(t4, t0) // 0 is one root
&& approximately_zero_when_compared_to(t4, t1)
&& approximately_zero_when_compared_to(t4, t2);
if (t4Or != t4And) {
SkDebugf("%s t4 or and\n", __FUNCTION__);
}
bool t3Or = approximately_zero_when_compared_to(t3, t0)
|| approximately_zero_when_compared_to(t3, t1)
|| approximately_zero_when_compared_to(t3, t2);
bool t3And = approximately_zero_when_compared_to(t3, t0)
&& approximately_zero_when_compared_to(t3, t1)
&& approximately_zero_when_compared_to(t3, t2);
if (t3Or != t3And) {
SkDebugf("%s t3 or and\n", __FUNCTION__);
}
bool t0Or = approximately_zero_when_compared_to(t0, t1) // 0 is one root
&& approximately_zero_when_compared_to(t0, t2)
&& approximately_zero_when_compared_to(t0, t3)
&& approximately_zero_when_compared_to(t0, t4);
bool t0And = approximately_zero_when_compared_to(t0, t1) // 0 is one root
&& approximately_zero_when_compared_to(t0, t2)
&& approximately_zero_when_compared_to(t0, t3)
&& approximately_zero_when_compared_to(t0, t4);
if (t0Or != t0And) {
SkDebugf("%s t0 or and\n", __FUNCTION__);
}
#endif
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, t2)) {
if (approximately_zero_when_compared_to(t3, t0)
&& approximately_zero_when_compared_to(t3, t1)
&& approximately_zero_when_compared_to(t3, t2)) {
return quadraticRootsReal(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) || approximately_zero(t1))// 0 is one root
// && approximately_zero_when_compared_to(t0, t2)
&& approximately_zero_when_compared_to(t0, t3)
&& approximately_zero_when_compared_to(t0, t4)) {
int num = cubicRootsReal(t4, t3, t2, t1, roots);
for (int i = 0; i < num; ++i) {
if (approximately_zero(roots[i])) {
return num;
}
}
roots[num++] = 0;
return num;
}
if (oneHint) {
SkASSERT(approximately_zero(t4 + t3 + t2 + t1 + t0)); // 1 is one root
int num = cubicRootsReal(t4, t4 + t3, -(t1 + t0), -t0, roots); // note that -C==A+B+D+E
for (int i = 0; i < num; ++i) {
if (approximately_equal(roots[i], 1)) {
return num;
}
}
roots[num++] = 1;
return num;
}
return -1;
}
int quarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
const double D, const double E, double s[4]) {
double u, v;
/* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
const double invA = 1 / A;
const double a = B * invA;
const double b = C * invA;
const double c = D * invA;
const double d = E * invA;
/* substitute x = y - a/4 to eliminate cubic term:
x^4 + px^2 + qx + r = 0 */
const double a2 = a * a;
const double p = -3 * a2 / 8 + b;
const double q = a2 * a / 8 - a * b / 2 + c;
const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
int num;
if (approximately_zero(r)) {
/* no absolute term: y(y^3 + py + q) = 0 */
num = cubicRootsReal(1, 0, p, q, s);
s[num++] = 0;
} else {
/* solve the resolvent cubic ... */
double cubicRoots[3];
int roots = cubicRootsReal(1, -p / 2, -r, r * p / 2 - q * q / 8, cubicRoots);
int index;
#if 0 && SK_DEBUG // enable to verify that any cubic root is as good as any other
double tries[3][4];
int nums[3];
for (index = 0; index < roots; ++index) {
/* ... and take one real solution ... */
const double z = cubicRoots[index];
/* ... to build two quadric equations */
u = z * z - r;
v = 2 * z - p;
if (approximately_zero_squared(u)) {
u = 0;
} else if (u > 0) {
u = sqrt(u);
} else {
SkDebugf("%s u=%1.9g <0\n", __FUNCTION__, u);
continue;
}
if (approximately_zero_squared(v)) {
v = 0;
} else if (v > 0) {
v = sqrt(v);
} else {
SkDebugf("%s v=%1.9g <0\n", __FUNCTION__, v);
continue;
}
nums[index] = quadraticRootsReal(1, q < 0 ? -v : v, z - u, tries[index]);
nums[index] += quadraticRootsReal(1, q < 0 ? v : -v, z + u, tries[index] + nums[index]);
/* resubstitute */
const double sub = a / 4;
for (int i = 0; i < nums[index]; ++i) {
tries[index][i] -= sub;
}
}
for (index = 0; index < roots; ++index) {
SkDebugf("%s", __FUNCTION__);
for (int idx2 = 0; idx2 < nums[index]; ++idx2) {
SkDebugf(" %1.9g", tries[index][idx2]);
}
SkDebugf("\n");
}
#endif
/* ... and take one real solution ... */
double z;
num = 0;
int num2 = 0;
for (index = firstCubicRoot; index < roots; ++index) {
z = cubicRoots[index];
/* ... to build two quadric equations */
u = z * z - r;
v = 2 * z - p;
if (approximately_zero_squared(u)) {
u = 0;
} else if (u > 0) {
u = sqrt(u);
} else {
continue;
}
if (approximately_zero_squared(v)) {
v = 0;
} else if (v > 0) {
v = sqrt(v);
} else {
continue;
}
num = quadraticRootsReal(1, q < 0 ? -v : v, z - u, s);
num2 = quadraticRootsReal(1, q < 0 ? v : -v, z + u, s + num);
if (!((num | num2) & 1)) {
break; // prefer solutions without single quad roots
}
}
num += num2;
if (!num) {
return 0; // no valid cubic root
}
}
/* resubstitute */
const double sub = a / 4;
for (int i = 0; i < num; ++i) {
s[i] -= sub;
}
// eliminate duplicates
for (int i = 0; i < num - 1; ++i) {
for (int j = i + 1; j < num; ) {
if (AlmostEqualUlps(s[i], s[j])) {
if (j < --num) {
s[j] = s[num];
}
} else {
++j;
}
}
}
return num;
}

View File

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

View File

@ -1,195 +0,0 @@
#include "CubicUtilities.h"
#include "Intersection_Tests.h"
#include "QuadraticUtilities.h"
#include "QuarticRoot.h"
double mulA[] = {-3, -1, 1, 3};
size_t mulACount = sizeof(mulA) / sizeof(mulA[0]);
double rootB[] = {-9, -6, -3, -1, 0, 1, 3, 6, 9};
size_t rootBCount = sizeof(rootB) / sizeof(rootB[0]);
double rootC[] = {-8, -6, -2, -1, 0, 1, 2, 6, 8};
size_t rootCCount = sizeof(rootC) / sizeof(rootC[0]);
double rootD[] = {-7, -4, -1, 0, 1, 2, 5};
size_t rootDCount = sizeof(rootD) / sizeof(rootD[0]);
double rootE[] = {-5, -1, 0, 1, 7};
size_t rootECount = sizeof(rootE) / sizeof(rootE[0]);
static void quadraticTest(bool limit) {
// (x - a)(x - b) == x^2 - (a + b)x + ab
for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) {
for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) {
for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) {
const double A = mulA[aIndex];
double B = rootB[bIndex];
double C = rootC[cIndex];
if (limit) {
B = (B - 6) / 12;
C = (C - 6) / 12;
}
const double b = A * (B + C);
const double c = A * B * C;
double roots[2];
const int rootCount = limit ? quadraticRootsValidT(A, b, c, roots)
: quadraticRootsReal(A, b, c, roots);
int expected;
if (limit) {
expected = B <= 0 && B >= -1;
expected += B != C && C <= 0 && C >= -1;
} else {
expected = 1 + (B != C);
}
SkASSERT(rootCount == expected);
if (!rootCount) {
continue;
}
SkASSERT(approximately_equal(roots[0], -B)
|| approximately_equal(roots[0], -C));
if (expected > 1) {
SkASSERT(!approximately_equal(roots[0], roots[1]));
SkASSERT(approximately_equal(roots[1], -B)
|| approximately_equal(roots[1], -C));
}
}
}
}
}
static void testOneCubic(bool limit, size_t aIndex, size_t bIndex, size_t cIndex, size_t dIndex) {
const double A = mulA[aIndex];
double B = rootB[bIndex];
double C = rootC[cIndex];
double D = rootD[dIndex];
if (limit) {
B = (B - 6) / 12;
C = (C - 6) / 12;
D = (C - 2) / 6;
}
const double b = A * (B + C + D);
const double c = A * (B * C + C * D + B * D);
const double d = A * B * C * D;
double roots[3];
const int rootCount = limit ? cubicRootsValidT(A, b, c, d, roots)
: cubicRootsReal(A, b, c, d, roots);
int expected;
if (limit) {
expected = B <= 0 && B >= -1;
expected += B != C && C <= 0 && C >= -1;
expected += B != D && C != D && D <= 0 && D >= -1;
} else {
expected = 1 + (B != C) + (B != D && C != D);
}
SkASSERT(rootCount == expected);
if (!rootCount) {
return;
}
SkASSERT(approximately_equal(roots[0], -B)
|| approximately_equal(roots[0], -C)
|| approximately_equal(roots[0], -D));
if (expected <= 1) {
return;
}
SkASSERT(!approximately_equal(roots[0], roots[1]));
SkASSERT(approximately_equal(roots[1], -B)
|| approximately_equal(roots[1], -C)
|| approximately_equal(roots[1], -D));
if (expected <= 2) {
return;
}
SkASSERT(!approximately_equal(roots[0], roots[2])
&& !approximately_equal(roots[1], roots[2]));
SkASSERT(approximately_equal(roots[2], -B)
|| approximately_equal(roots[2], -C)
|| approximately_equal(roots[2], -D));
}
static void cubicTest(bool limit) {
// (x - a)(x - b)(x - c) == x^3 - (a + b + c)x^2 + (ab + bc + ac)x - abc
for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) {
for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) {
for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) {
for (size_t dIndex = 0; dIndex < rootDCount; ++dIndex) {
testOneCubic(limit, aIndex, bIndex, cIndex, dIndex);
}
}
}
}
}
static void testOneQuartic(size_t aIndex, size_t bIndex, size_t cIndex, size_t dIndex,
size_t eIndex) {
const double A = mulA[aIndex];
const double B = rootB[bIndex];
const double C = rootC[cIndex];
const double D = rootD[dIndex];
const double E = rootE[eIndex];
const double b = A * (B + C + D + E);
const double c = A * (B * C + C * D + B * D + B * E + C * E + D * E);
const double d = A * (B * C * D + B * C * E + B * D * E + C * D * E);
const double e = A * B * C * D * E;
double roots[4];
bool oneHint = approximately_zero(A + b + c + d + e);
int rootCount = reducedQuarticRoots(A, b, c, d, e, oneHint, roots);
if (rootCount < 0) {
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);
SkASSERT(rootCount == expected);
SkASSERT(AlmostEqualUlps(roots[0], -B)
|| AlmostEqualUlps(roots[0], -C)
|| AlmostEqualUlps(roots[0], -D)
|| AlmostEqualUlps(roots[0], -E));
if (expected <= 1) {
return;
}
SkASSERT(!AlmostEqualUlps(roots[0], roots[1]));
SkASSERT(AlmostEqualUlps(roots[1], -B)
|| AlmostEqualUlps(roots[1], -C)
|| AlmostEqualUlps(roots[1], -D)
|| AlmostEqualUlps(roots[1], -E));
if (expected <= 2) {
return;
}
SkASSERT(!AlmostEqualUlps(roots[0], roots[2])
&& !AlmostEqualUlps(roots[1], roots[2]));
SkASSERT(AlmostEqualUlps(roots[2], -B)
|| AlmostEqualUlps(roots[2], -C)
|| AlmostEqualUlps(roots[2], -D)
|| AlmostEqualUlps(roots[2], -E));
if (expected <= 3) {
return;
}
SkASSERT(!AlmostEqualUlps(roots[0], roots[3])
&& !AlmostEqualUlps(roots[1], roots[3])
&& !AlmostEqualUlps(roots[2], roots[3]));
SkASSERT(AlmostEqualUlps(roots[3], -B)
|| AlmostEqualUlps(roots[3], -C)
|| AlmostEqualUlps(roots[3], -D)
|| AlmostEqualUlps(roots[3], -E));
}
static void quarticTest() {
// (x - a)(x - b)(x - c)(x - d) == x^4 - (a + b + c + d)x^3
// + (ab + bc + cd + ac + bd + cd)x^2 - (abc + bcd + abd + acd) * x + abcd
for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) {
for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) {
for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) {
for (size_t dIndex = 0; dIndex < rootDCount; ++dIndex) {
for (size_t eIndex = 0; eIndex < rootECount; ++eIndex) {
testOneQuartic(aIndex, bIndex, cIndex, dIndex, eIndex);
}
}
}
}
}
}
void QuarticRoot_Test() {
testOneCubic(false, 0, 5, 5, 4);
testOneQuartic(0, 0, 2, 4, 3);
quadraticTest(true);
quadraticTest(false);
cubicTest(true);
cubicTest(false);
quarticTest();
}

View File

@ -1,106 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "ShapeOps.h"
// four rects, of four sizes
// for 3 smaller sizes, tall, wide
// top upper mid lower bottom aligned (3 bits, 5 values)
// same with x (3 bits, 5 values)
// not included, square, tall, wide (2 bits)
// cw or ccw (1 bit)
int failSet[][8] = {
{ 0, 1, 0, 6, 2, 3, 1, 4 }
};
static void* testShapeOps4x4CubicsMain(void* data)
{
SkASSERT(data);
State4& state = *(State4*) data;
char pathStr[1024]; // gdb: set print elements 400
bzero(pathStr, sizeof(pathStr));
do {
for (int a = 0 ; a < 6; ++a) {
for (int b = a + 1 ; b < 7; ++b) {
for (int c = 0 ; c < 6; ++c) {
for (int d = c + 1 ; d < 7; ++d) {
for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) {
#if 0
if (state.a == fail[0] && state.b == fail[1] && state.c == fail[2] && state.d == fail[3]
&& a == fail[4] && b == fail[5] && c == fail[6] && d == fail[7]) {
SkDebugf("skip failing case\n");
}
// skip this troublesome cubic pair
#endif
SkPath pathA, pathB;
char* str = pathStr;
pathA.setFillType((SkPath::FillType) e);
str += sprintf(str, " path.setFillType(SkPath::k%s_FillType);\n",
e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
? "EvenOdd" : "?UNDEFINED");
pathA.moveTo(state.a, state.b);
str += sprintf(str, " path.moveTo(%d,%d);\n", state.a, state.b);
pathA.cubicTo(state.c, state.d, b, a, d, c);
str += sprintf(str, " path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.c, state.d,
b, a, d, c);
pathA.close();
str += sprintf(str, " path.close();\n");
pathB.setFillType((SkPath::FillType) f);
str += sprintf(str, " pathB.setFillType(SkPath::k%s_FillType);\n",
f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
? "EvenOdd" : "?UNDEFINED");
pathB.moveTo(a, b);
str += sprintf(str, " pathB.moveTo(%d,%d);\n", a, b);
pathB.cubicTo(c, d, state.b, state.a, state.d, state.c);
str += sprintf(str, " pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
state.b, state.a, state.d, state.c);
pathB.close();
str += sprintf(str, " pathB.close();\n");
for (int op = 0 ; op < kShapeOp_Count; ++op) {
outputProgress(state, pathStr, (ShapeOp) op);
testShapeOp(pathA, pathB, (ShapeOp) op);
state.testsRun++;
}
}
}
}
}
}
}
} while (runNextTestSet(state));
return NULL;
}
void ShapeOps4x4CubicsThreaded_Test(int& testsRun)
{
SkDebugf("%s\n", __FUNCTION__);
#ifdef SK_DEBUG
gDebugMaxWindSum = 4;
gDebugMaxWindValue = 4;
#endif
const char testLineStr[] = "cubicOp";
initializeTests(testLineStr, sizeof(testLineStr));
int testsStart = testsRun;
for (int a = 0; a < 6; ++a) { // outermost
for (int b = a + 1; b < 7; ++b) {
for (int c = 0 ; c < 6; ++c) {
for (int d = c + 1; d < 7; ++d) {
testsRun += dispatchTest4(testShapeOps4x4CubicsMain, a, b, c, d);
}
if (!gRunTestsInOneThread) SkDebugf(".");
}
if (!gRunTestsInOneThread) SkDebugf("%d", b);
}
if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
}
testsRun += waitForCompletion();
SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
}

View File

@ -1,94 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "ShapeOps.h"
// four rects, of four sizes
// for 3 smaller sizes, tall, wide
// top upper mid lower bottom aligned (3 bits, 5 values)
// same with x (3 bits, 5 values)
// not included, square, tall, wide (2 bits)
// cw or ccw (1 bit)
static void* testShapeOps4x4RectsMain(void* data)
{
SkASSERT(data);
State4& state = *(State4*) data;
char pathStr[1024]; // gdb: set print elements 400
bzero(pathStr, sizeof(pathStr));
do {
for (int a = 0 ; a < 6; ++a) {
for (int b = a + 1 ; b < 7; ++b) {
for (int c = 0 ; c < 6; ++c) {
for (int d = c + 1 ; d < 7; ++d) {
for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) {
SkPath pathA, pathB;
char* str = pathStr;
pathA.setFillType((SkPath::FillType) e);
str += sprintf(str, " path.setFillType(SkPath::k%s_FillType);\n",
e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
? "EvenOdd" : "?UNDEFINED");
pathA.addRect(state.a, state.a, state.b, state.b, SkPath::kCW_Direction);
str += sprintf(str, " path.addRect(%d, %d, %d, %d,"
" SkPath::kCW_Direction);\n", state.a, state.a, state.b, state.b);
pathA.addRect(state.c, state.c, state.d, state.d, SkPath::kCW_Direction);
str += sprintf(str, " path.addRect(%d, %d, %d, %d,"
" SkPath::kCW_Direction);\n", state.c, state.c, state.d, state.d);
pathA.close();
pathB.setFillType((SkPath::FillType) f);
str += sprintf(str, " pathB.setFillType(SkPath::k%s_FillType);\n",
f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
? "EvenOdd" : "?UNDEFINED");
pathB.addRect(a, a, b, b, SkPath::kCW_Direction);
str += sprintf(str, " pathB.addRect(%d, %d, %d, %d,"
" SkPath::kCW_Direction);\n", a, a, b, b);
pathB.addRect(c, c, d, d, SkPath::kCW_Direction);
str += sprintf(str, " pathB.addRect(%d, %d, %d, %d,"
" SkPath::kCW_Direction);\n", c, c, d, d);
pathB.close();
for (int op = 0 ; op < kShapeOp_Count; ++op) {
outputProgress(state, pathStr, (ShapeOp) op);
testShapeOp(pathA, pathB, (ShapeOp) op);
state.testsRun++;
}
}
}
}
}
}
}
} while (runNextTestSet(state));
return NULL;
}
void ShapeOps4x4RectsThreaded_Test(int& testsRun)
{
SkDebugf("%s\n", __FUNCTION__);
#ifdef SK_DEBUG
gDebugMaxWindSum = 4;
gDebugMaxWindValue = 4;
#endif
const char testLineStr[] = "testOp";
initializeTests(testLineStr, sizeof(testLineStr));
int testsStart = testsRun;
for (int a = 0; a < 6; ++a) { // outermost
for (int b = a + 1; b < 7; ++b) {
for (int c = 0 ; c < 6; ++c) {
for (int d = c + 1; d < 7; ++d) {
testsRun += dispatchTest4(testShapeOps4x4RectsMain, a, b, c, d);
}
if (!gRunTestsInOneThread) SkDebugf(".");
}
if (!gRunTestsInOneThread) SkDebugf("%d", b);
}
if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
}
testsRun += waitForCompletion();
SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
}

View File

@ -1,279 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Simplify.h"
namespace Op {
#define INCLUDED_BY_SHAPE_OPS 1
#include "Simplify.cpp"
// FIXME: this and find chase should be merge together, along with
// other code that walks winding in angles
// OPTIMIZATION: Probably, the walked winding should be rolled into the angle structure
// so it isn't duplicated by walkers like this one
static Segment* findChaseOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd) {
while (chase.count()) {
Span* span;
chase.pop(&span);
const Span& backPtr = span->fOther->span(span->fOtherIndex);
Segment* segment = backPtr.fOther;
nextStart = backPtr.fOtherIndex;
SkTDArray<Angle> angles;
int done = 0;
if (segment->activeAngle(nextStart, done, angles)) {
Angle* last = angles.end() - 1;
nextStart = last->start();
nextEnd = last->end();
#if TRY_ROTATE
*chase.insert(0) = span;
#else
*chase.append() = span;
#endif
return last->segment();
}
if (done == angles.count()) {
continue;
}
SkTDArray<Angle*> sorted;
bool sortable = Segment::SortAngles(angles, sorted);
int angleCount = sorted.count();
#if DEBUG_SORT
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0);
#endif
if (!sortable) {
continue;
}
// find first angle, initialize winding to computed fWindSum
int firstIndex = -1;
const Angle* angle;
do {
angle = sorted[++firstIndex];
segment = angle->segment();
} while (segment->windSum(angle) == SK_MinS32);
#if DEBUG_SORT
segment->debugShowSort(__FUNCTION__, sorted, firstIndex);
#endif
int sumMiWinding = segment->updateWindingReverse(angle);
int sumSuWinding = segment->updateOppWindingReverse(angle);
if (segment->operand()) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
int nextIndex = firstIndex + 1;
int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
Segment* first = NULL;
do {
SkASSERT(nextIndex != firstIndex);
if (nextIndex == angleCount) {
nextIndex = 0;
}
angle = sorted[nextIndex];
segment = angle->segment();
int start = angle->start();
int end = angle->end();
int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
segment->setUpWindings(start, end, sumMiWinding, sumSuWinding,
maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
if (!segment->done(angle)) {
if (!first) {
first = segment;
nextStart = start;
nextEnd = end;
}
(void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
oppSumWinding, true, angle);
}
} while (++nextIndex != lastIndex);
if (first) {
#if TRY_ROTATE
*chase.insert(0) = span;
#else
*chase.append() = span;
#endif
return first;
}
}
return NULL;
}
/*
static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
bool windingIsOp, ShapeOp op) {
bool active = windingIsActive(winding, spanWinding);
if (!active) {
return false;
}
if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
switch (op) {
case kIntersect_Op:
case kUnion_Op:
return true;
case kDifference_Op: {
int absSpan = abs(spanWinding);
int absOpp = abs(oppSpanWinding);
return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
}
case kXor_Op:
return spanWinding != oppSpanWinding;
default:
SkASSERT(0);
}
}
bool opActive = oppWinding != 0;
return gOpLookup[op][opActive][windingIsOp];
}
*/
static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
const int xorMask, const int xorOpMask, PathWrapper& simple) {
bool firstContour = true;
bool unsortable = false;
bool topUnsortable = false;
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
do {
int index, endIndex;
bool done;
Segment* current = findSortableTop(contourList, firstContour, index, endIndex, topLeft,
topUnsortable, done, true);
if (!current) {
if (topUnsortable || !done) {
topUnsortable = false;
SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
topLeft.fX = topLeft.fY = SK_ScalarMin;
continue;
}
break;
}
SkTDArray<Span*> chaseArray;
do {
if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
do {
#if DEBUG_ACTIVE_SPANS
if (!unsortable && current->done()) {
debugShowActiveSpans(contourList);
}
#endif
SkASSERT(unsortable || !current->done());
int nextStart = index;
int nextEnd = endIndex;
Segment* next = current->findNextOp(chaseArray, nextStart, nextEnd,
unsortable, op, xorMask, xorOpMask);
if (!next) {
if (!unsortable && simple.hasMove()
&& current->verb() != SkPath::kLine_Verb
&& !simple.isClosed()) {
current->addCurveTo(index, endIndex, simple, true);
SkASSERT(simple.isClosed());
}
break;
}
#if DEBUG_FLOW
SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
#endif
current->addCurveTo(index, endIndex, simple, true);
current = next;
index = nextStart;
endIndex = nextEnd;
} while (!simple.isClosed() && ((!unsortable)
|| !current->done(SkMin32(index, endIndex))));
if (current->activeWinding(index, endIndex) && !simple.isClosed()) {
SkASSERT(unsortable);
int min = SkMin32(index, endIndex);
if (!current->done(min)) {
current->addCurveTo(index, endIndex, simple, true);
current->markDoneBinary(min);
}
}
simple.close();
} else {
Span* last = current->markAndChaseDoneBinary(index, endIndex);
if (last && !last->fLoop) {
*chaseArray.append() = last;
}
}
current = findChaseOp(chaseArray, index, endIndex);
#if DEBUG_ACTIVE_SPANS
debugShowActiveSpans(contourList);
#endif
if (!current) {
break;
}
} while (true);
} while (true);
return simple.someAssemblyRequired();
}
} // end of Op namespace
void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
#if DEBUG_SORT || DEBUG_SWAP_TOP
Op::gDebugSortCount = Op::gDebugSortCountDefault;
#endif
result.reset();
result.setFillType(SkPath::kEvenOdd_FillType);
// turn path into list of segments
SkTArray<Op::Contour> contours;
// FIXME: add self-intersecting cubics' T values to segment
Op::EdgeBuilder builder(one, contours);
const int xorMask = builder.xorMask();
builder.addOperand(two);
builder.finish();
const int xorOpMask = builder.xorMask();
SkTDArray<Op::Contour*> contourList;
makeContourList(contours, contourList, xorMask == kEvenOdd_Mask,
xorOpMask == kEvenOdd_Mask);
Op::Contour** currentPtr = contourList.begin();
if (!currentPtr) {
return;
}
Op::Contour** listEnd = contourList.end();
// find all intersections between segments
do {
Op::Contour** nextPtr = currentPtr;
Op::Contour* current = *currentPtr++;
if (current->containsCubics()) {
addSelfIntersectTs(current);
}
Op::Contour* next;
do {
next = *nextPtr++;
} while (addIntersectTs(current, next) && nextPtr != listEnd);
} while (currentPtr != listEnd);
// eat through coincident edges
int total = 0;
int index;
for (index = 0; index < contourList.count(); ++index) {
total += contourList[index]->segments().count();
}
#if DEBUG_SHOW_WINDING
Op::Contour::debugShowWindingValues(contourList);
#endif
coincidenceCheck(contourList, total);
#if DEBUG_SHOW_WINDING
Op::Contour::debugShowWindingValues(contourList);
#endif
fixOtherTIndex(contourList);
sortSegments(contourList);
#if DEBUG_ACTIVE_SPANS
debugShowActiveSpans(contourList);
#endif
// construct closed contours
Op::PathWrapper wrapper(result);
bridgeOp(contourList, op, xorMask, xorOpMask, wrapper);
{ // if some edges could not be resolved, assemble remaining fragments
SkPath temp;
temp.setFillType(SkPath::kEvenOdd_FillType);
Op::PathWrapper assembled(temp);
assemble(wrapper, assembled);
result = *assembled.nativePath();
}
}

Some files were not shown because too many files have changed in this diff Show More