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:
parent
f75a130c45
commit
17a2b5473d
@ -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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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:
|
||||
;
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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]);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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]);
|
@ -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;
|
@ -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;
|
||||
}
|
@ -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& );
|
@ -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"
|
@ -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
|
||||
*/
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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 = ⊂
|
||||
}
|
||||
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);
|
||||
}
|
@ -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__);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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__
|
@ -1,7 +0,0 @@
|
||||
#ifndef __DataTypes_Test_h__
|
||||
#define __DataTypes_Test_h__
|
||||
|
||||
const double PointEpsilon = 0.000001;
|
||||
const double SquaredEpsilon = PointEpsilon * PointEpsilon;
|
||||
|
||||
#endif
|
@ -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);
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
class SkCanvas;
|
||||
|
||||
bool DrawEdgeDemo(SkCanvas* canvas, int step, bool useOld);
|
@ -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>
|
@ -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
@ -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
@ -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
@ -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;
|
||||
}
|
||||
}
|
@ -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])();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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])();
|
||||
}
|
||||
}
|
@ -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();
|
@ -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;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
@ -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]);
|
@ -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();
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
@ -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& );
|
@ -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;
|
||||
}
|
@ -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
|
@ -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]);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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]);
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
};
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
@ -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]);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
@ -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
|
||||
}
|
@ -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();
|
||||
}
|
@ -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]);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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]);
|
@ -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;
|
@ -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;
|
||||
}
|
@ -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& );
|
@ -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"
|
@ -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];
|
||||
};
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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]);
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user