shape ops work in progress

git-svn-id: http://skia.googlecode.com/svn/trunk@8137 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
caryclark@google.com 2013-03-13 20:29:41 +00:00
parent dcf9c19d38
commit 1304bb25aa
27 changed files with 5507 additions and 515 deletions

View File

@ -107,7 +107,7 @@ int main (int argc, char * const argv[]) {
const char forceReleaseReplace[] =
"#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging"
;
if (!replace(argv[0], dir, "DataTypes.cpp", forceReleaseMarker, NULL, forceReleaseReplace,
if (!replace(argv[0], dir, "DataTypes.h", forceReleaseMarker, NULL, forceReleaseReplace,
sizeof(forceReleaseReplace) - 1)) {
return 0;
}

View File

@ -12,10 +12,11 @@
#include "LineIntersection.h"
#include "LineUtilities.h"
#include "QuadraticUtilities.h"
#include "TSearch.h"
#if ONE_OFF_DEBUG
static const double tLimits1[2][2] = {{0.86731567, 0.867316052}, {0.912837526, 0.912837908}};
static const double tLimits2[2][2] = {{0.83051487, 0.830515252}, {0.860977985, 0.860978367}};
static const double tLimits1[2][2] = {{0.328432751, 0.328433132}, {0.556114578, 0.55611496}};
static const double tLimits2[2][2] = {{-0.83051487, -0.830515252}, {-0.860977985, -0.860978367}};
#endif
#define DEBUG_QUAD_PART 0
@ -77,6 +78,7 @@ static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic&
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);
@ -103,8 +105,8 @@ static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic&
if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1
&& tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) {
Cubic cSub1, cSub2;
sub_divide(cubic1, t1Start, tEnd1, cSub1);
sub_divide(cubic2, t2Start, tEnd2, 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;
@ -121,9 +123,8 @@ static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic&
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, p2;
xy_at_t(cubic1, to1, p1.x, p1.y);
xy_at_t(cubic2, to2, p2.x, p2.y);
_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) {
@ -297,18 +298,29 @@ static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic&
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 (line[0].approximatelyEqual(cubic2[0]) || line[0].approximatelyEqual(cubic2[3])) {
#if 0
if (!selfIntersect && (line[0].approximatelyEqual(cubic2[0])
|| line[0].approximatelyEqual(cubic2[3]))) {
return false;
}
double tMin = 1, tMax = 0;
#endif
bool result = false;
SkTDArray<double> tVals; // OPTIMIZE: replace with hard-sized array
for (int index = 0; index < 4; ++index) {
if (index == t1Index) {
continue;
@ -325,19 +337,48 @@ static bool intersectEnd(const Cubic& cubic1, bool start, const Cubic& cubic2, c
if (!intersect(cubic2, line, local)) {
continue;
}
for (int index = 0; index < local.fUsed; ++index) {
tMin = SkTMin(tMin, local.fT[0][index]);
tMax = SkTMax(tMax, local.fT[0][index]);
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 (tMin > tMax) {
return false;
if (tVals.count() == 0) {
return result;
}
double tMin1 = start ? 0 : 1 - 1.0 / gPrecisionUnit;
double tMax1 = start ? 1.0 / gPrecisionUnit : 1;
double tMin2 = SkTMax(tMin - 1.0 / gPrecisionUnit, 0.0);
double tMax2 = SkTMin(tMax + 1.0 / gPrecisionUnit, 1.0);
return intersect3(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i);
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;
@ -367,10 +408,13 @@ bool intersect3(const Cubic& c1, const Cubic& c2, Intersections& i) {
c2Bounds.setBounds(c2);
result |= intersectEnd(c1, false, c2, c2Bounds, i);
result |= intersectEnd(c1, true, c2, c2Bounds, i);
i.swap();
result |= intersectEnd(c2, false, c1, c1Bounds, i);
result |= intersectEnd(c2, true, c1, c1Bounds, i);
i.swap();
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.
@ -408,10 +452,15 @@ form dotted with the normal, and solving for the roots. My math foo is too poor
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 ((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))) {
if (ends_are_extrema_in_x_or_y(c)) {
return false;
}
(void) intersect3(c, c, i);
if (i.used() > 0) {
SkASSERT(i.used() == 1);
if (i.fT[0][0] > i.fT[1][0]) {
SkTSwap(i.fT[0][0], i.fT[1][0]);
}
}
return i.used();
}

View File

@ -136,6 +136,30 @@ static const Cubic testSet[] = {
const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
static const Cubic newTestSet[] = {
{{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}},
@ -177,22 +201,22 @@ static void oneOff(const Cubic& cubic1, const Cubic& cubic2) {
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);
}
SkDebugf("\n");
#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);
}
SkDebugf("\n");
#endif
Intersections intersections2, intersections3;
intersect2(cubic1, cubic2, intersections2);
@ -257,25 +281,34 @@ static void oneOff(const Cubic& cubic1, const Cubic& cubic2) {
#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);
}
SkDebugf("\n");
#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);
}
SkDebugf("\n");
#endif
Intersections intersections3;
intersect3(cubic1, cubic2, intersections3);
@ -284,7 +317,7 @@ static void oneOff3(const Cubic& cubic1, const Cubic& cubic2) {
_Point xy1, xy2;
for (pt3 = 0; pt3 < intersections3.used(); ++pt3) {
double tt3 = intersections3.fT[0][pt3];
SkASSERT(!approximately_equal(last, tt3));
// SkASSERT(!approximately_equal(last, tt3));
last = tt3;
tt1 = intersections3.fT[0][pt3];
xy_at_t(cubic1, tt1, xy1.x, xy1.y);
@ -670,12 +703,12 @@ static void intersectionFinder(int index0, int index1, double t1Seed, double t2S
void CubicIntersection_IntersectionFinder() {
double t1Seed = 0.867315861;
double t2Seed = 0.912837717;
double t1Step = 0.01;
double t2Step = 0.01;
intersectionFinder(0, 1, t1Seed, t2Seed, t1Step, t2Step);
intersectionFinder(0, 1, 0.830515061, 0.860978176, t1Step, t2Step);
double t1Seed = 0.5;
double t2Seed = 0.3;
double t1Step = 0.1;
double t2Step = 0.1;
if (false) intersectionFinder(0, 1, t1Seed, t2Seed, t1Step, t2Step);
intersectionFinder(1, 0, .3, .5, t1Step, t2Step);
}
static void coincidentTest() {
@ -687,6 +720,8 @@ static void coincidentTest() {
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}},
@ -696,17 +731,29 @@ void CubicIntersection_SelfTest() {
for (size_t index = 0; 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 (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);
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;
int result = intersect(cubic, 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]));

View File

@ -65,6 +65,13 @@ static double interp_cubic_coords(const double* src, double t)
}
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);

View File

@ -48,6 +48,7 @@ http://www.caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
#include "CubicUtilities.h"
#include "CurveIntersection.h"
#include "LineIntersection.h"
#include "TSearch.h"
const bool AVERAGE_END_POINTS = true; // results in better fitting curves
@ -147,9 +148,34 @@ void cubic_to_quadratics(const Cubic& cubic, double precision, SkTDArray<double>
if (order < 3) {
return;
}
double inflectT[2];
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]);
@ -174,17 +200,17 @@ void cubic_to_quadratics(const Cubic& cubic, double precision, SkTDArray<double>
addTs(pair.second(), precision, inflectT[0], 1, ts);
return;
}
if (inflections == 2) {
if (inflectT[0] > inflectT[1]) {
SkTSwap(inflectT[0], inflectT[1]);
}
if (inflections > 1) {
Cubic part;
sub_divide(cubic, 0, inflectT[0], part);
addTs(part, precision, 0, inflectT[0], ts);
sub_divide(cubic, inflectT[0], inflectT[1], part);
addTs(part, precision, inflectT[0], inflectT[1], ts);
sub_divide(cubic, inflectT[1], 1, part);
addTs(part, precision, inflectT[1], 1, ts);
int last = inflections - 1;
for (int idx = 0; idx < last; ++idx) {
sub_divide(cubic, inflectT[idx], inflectT[idx + 1], part);
addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
}
sub_divide(cubic, inflectT[last], 1, part);
addTs(part, precision, inflectT[last], 1, ts);
return;
}
addTs(cubic, precision, 0, 1, ts);

View File

@ -6,6 +6,7 @@
*/
#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
@ -27,6 +28,13 @@ double calcPrecision(const Cubic& cubic, double t, double scale) {
}
#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
@ -38,6 +46,59 @@ void coefficients(const double* cubic, double& A, double& B, double& C, double&
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);
@ -241,6 +302,7 @@ _Vector dxdy_at_t(const Cubic& cubic, double 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;
@ -249,9 +311,45 @@ int find_cubic_inflections(const Cubic& src, double tValues[])
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);
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;
@ -287,7 +385,7 @@ _Point top(const Cubic& cubic, double startT, double endT) {
topPt = sub[3];
}
double extremeTs[2];
if (!between(sub[0].y, sub[1].y, sub[3].y) && !between(sub[0].y, sub[2].y, sub[3].y)) {
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;

View File

@ -15,11 +15,13 @@ double calcPrecision(const Cubic& cubic);
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);
@ -27,8 +29,12 @@ 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);

View File

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

View File

@ -12,7 +12,7 @@
#include "SkPoint.h"
#define FORCE_RELEASE 1 // set force release to 1 for multiple thread -- no debugging
#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging
#define ONE_OFF_DEBUG 0
#define ONE_OFF_DEBUG_MATHEMATICA 0
@ -43,10 +43,8 @@ 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
#ifdef SK_DEBUG
const double ROUGH_EPSILON = FLT_EPSILON * 16;
#endif
const double ROUGH_EPSILON = FLT_EPSILON * 32;
const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256;
inline bool approximately_zero(double x) {
return fabs(x) < FLT_EPSILON;
@ -196,11 +194,13 @@ inline bool between(double a, double b, double c) {
return (a - b) * (c - b) <= 0;
}
#ifdef SK_DEBUG
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;
}
#endif
struct _Point;
@ -326,6 +326,10 @@ struct _Point {
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);
}

View File

@ -14,12 +14,13 @@ void parseSVG();
void Intersection_Tests() {
int testsRun = 0;
QuadraticIntersection_IntersectionFinder();
QuadraticIntersection_OneOffTest();
CubicIntersection_IntersectionFinder();
CubicIntersection_NewOneOffTest();
#if 0
CubicUtilities_Test();
CubicIntersection_SelfTest();
QuadraticIntersection_IntersectionFinder();
QuadraticIntersection_OneOffTest();
#endif
SimplifyNew_Test();
CubicsToQuadratics_OneOffTest();

View File

@ -25,6 +25,7 @@ void CubicsToQuadratics_OneOffTest();
void CubicsToQuadratics_OneOffTests();
void CubicsToQuadratics_RandTest();
void CubicToQuadratics_Test();
void CubicUtilities_Test();
void Inline_Tests();
void Intersection_Tests();
void LineCubicIntersection_Test();

View File

@ -93,10 +93,24 @@ 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 midT = (fT[0][index] + one) / 2;
if (approximately_equal(midT, one) || pt.approximatelyEqual(fPt[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;
}

View File

@ -229,21 +229,21 @@ void flip() {
}
}
bool pinTs(double& cubicT, double& lineT) {
static bool pinTs(double& cubicT, double& lineT) {
if (!approximately_one_or_less(lineT)) {
return false;
}
if (!approximately_zero_or_more(lineT)) {
return false;
}
if (cubicT < 0) {
if (precisely_less_than_zero(cubicT)) {
cubicT = 0;
} else if (cubicT > 1) {
} else if (precisely_greater_than_one(cubicT)) {
cubicT = 1;
}
if (lineT < 0) {
if (precisely_less_than_zero(lineT)) {
lineT = 0;
} else if (lineT > 1) {
} else if (precisely_greater_than_one(lineT)) {
lineT = 1;
}
return true;

View File

@ -264,21 +264,21 @@ void flip() {
}
}
bool pinTs(double& quadT, double& lineT) {
static bool pinTs(double& quadT, double& lineT) {
if (!approximately_one_or_less(lineT)) {
return false;
}
if (!approximately_zero_or_more(lineT)) {
return false;
}
if (quadT < 0) {
if (precisely_less_than_zero(quadT)) {
quadT = 0;
} else if (quadT > 1) {
} else if (precisely_greater_than_one(quadT)) {
quadT = 1;
}
if (lineT < 0) {
if (precisely_less_than_zero(lineT)) {
lineT = 0;
} else if (lineT > 1) {
} else if (precisely_greater_than_one(lineT)) {
lineT = 1;
}
return true;

View File

@ -1,8 +0,0 @@
/*
* LogoPlay.h
* shapeops_edge
*
* Created by Cary Clark on 2/6/13.
* Copyright 2013 __MyCompanyName__. All rights reserved.
*
*/

View File

@ -188,7 +188,7 @@ static bool isLinearInner(const Quadratic& q1, double t1s, double t1e, const Qua
} else if (tCount > 1) {
QSort<double>(tsFound.begin(), tsFound.end() - 1);
tMin = tsFound[0];
tMax = tsFound[1];
tMax = tsFound[tsFound.count() - 1];
}
_Point end;
xy_at_t(q2, t2s, end.x, end.y);
@ -474,7 +474,7 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
if (r1Count == 1) {
if (pts1[0].approximatelyEqualHalf(pts2[0])) {
i.insert(roots1Copy[0], roots2Copy[0], pts1[0]);
} else if (pts1[0].roughlyEqual(pts2[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) {

View File

@ -54,6 +54,18 @@ static void standardTestCases() {
}
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}},
@ -223,6 +235,7 @@ static void oneOffTest1(size_t outer, size_t inner) {
void QuadraticIntersection_OneOffTest() {
oneOffTest1(0, 1);
oneOffTest1(1, 0);
}
static void oneOffTests() {
@ -251,8 +264,8 @@ static void coincidentTest() {
SkASSERT(intersections2.coincidentUsed() == 2);
SkASSERT(intersections2.used() == 2);
for (int pt = 0; pt < intersections2.coincidentUsed(); ++pt) {
double tt1 = intersections2.fT[0][pt];
double tt2 = intersections2.fT[1][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));
}
@ -328,10 +341,10 @@ static void intersectionFinder(int test1, int test2) {
const Quadratic& quad1 = testSet[test1];
const Quadratic& quad2 = testSet[test2];
double t1Seed = 0.966;
double t2Seed = 0.99;
double t1Seed = 0.5;
double t2Seed = 0.8;
double t1Step = 0.1;
double t2Step = 0.01;
double t2Step = 0.1;
_Point t1[3], t2[3];
bool toggle = true;
do {

View File

@ -22,8 +22,8 @@ static void oneOffTest() {
for (size_t index = 0; index < testSetCount; ++index) {
const Quadratic& quad = testSet[index];
Quadratic reduce;
int order = reduceOrder(quad, reduce, kReduceOrder_TreatAsFill);
SkASSERT(order == 3);
SkDEBUGCODE(int result = ) reduceOrder(quad, reduce, kReduceOrder_TreatAsFill);
SkASSERT(result == 3);
}
}

View File

@ -15,6 +15,10 @@
// 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);
@ -28,6 +32,14 @@ static void* testShapeOps4x4CubicsMain(void* data)
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);

View File

@ -214,7 +214,7 @@ static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
#if DEBUG_SORT
#if DEBUG_SORT || DEBUG_SWAP_TOP
Op::gDebugSortCount = Op::gDebugSortCountDefault;
#endif
result.reset();
@ -269,4 +269,11 @@ void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
// 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();
}
}

View File

@ -64,7 +64,7 @@ const bool gRunTestsInOneThread = true;
#define DEBUG_ACTIVE_OP 1
#define DEBUG_ACTIVE_SPANS 1
#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
#define DEBUG_ACTIVE_SPANS_SHORT_FORM 0
#define DEBUG_ADD_INTERSECTING_TS 1
#define DEBUG_ADD_T_PAIR 1
#define DEBUG_ANGLE 1
@ -113,7 +113,7 @@ static int gContourID;
static int gSegmentID;
#endif
#if DEBUG_SORT
#if DEBUG_SORT || DEBUG_SWAP_TOP
static int gDebugSortCountDefault = SK_MaxS32;
static int gDebugSortCount;
#endif
@ -881,7 +881,6 @@ public:
QuadSubDivideHD(fPts, startT, endT, quad);
fTangent1.quadEndPoints(quad, 0, 1);
if (dx() == 0 && dy() == 0) {
// SkDebugf("*** %s quad is line\n", __FUNCTION__);
fTangent1.quadEndPoints(quad);
}
fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- compare sign only
@ -894,7 +893,6 @@ public:
fTangent1.cubicEndPoints(fCurvePart, 0, 2);
nextC = 3;
if (dx() == 0 && dy() == 0) {
// SkDebugf("*** %s cubic is line\n", __FUNCTION__);
fTangent1.cubicEndPoints(fCurvePart, 0, 3);
}
}
@ -1036,7 +1034,7 @@ struct Bounds : public SkRect {
|| sk_double_isnan(fLeft) || sk_double_isnan(fRight)
|| sk_double_isnan(fTop) || sk_double_isnan(fBottom);
}
void setCubicBounds(const SkPoint a[4]) {
_Rect dRect;
MAKE_CONST_CUBIC(cubic, a);
@ -1045,6 +1043,11 @@ struct Bounds : public SkRect {
(float) dRect.bottom);
}
void setLineBounds(const SkPoint a[2]) {
setPoint(a[0]);
add(a[1]);
}
void setQuadBounds(const SkPoint a[3]) {
MAKE_CONST_QUAD(quad, a);
_Rect dRect;
@ -1059,6 +1062,13 @@ struct Bounds : public SkRect {
}
};
static void (Bounds::*setSegmentBounds[])(const SkPoint[]) = {
NULL,
&Bounds::setLineBounds,
&Bounds::setQuadBounds,
&Bounds::setCubicBounds
};
// OPTIMIZATION: does the following also work, and is it any faster?
// return outerWinding * innerWinding > 0
// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
@ -2914,51 +2924,57 @@ public:
buildAngles(firstT, angles, true);
SkTDArray<Angle*> sorted;
bool sortable = SortAngles(angles, sorted);
#if DEBUG_SORT
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
int first = SK_MaxS32;
SkScalar top = SK_ScalarMax;
int count = sorted.count();
for (int index = 0; index < count; ++index) {
const Angle* angle = sorted[index];
Segment* next = angle->segment();
Bounds bounds;
next->subDivideBounds(angle->end(), angle->start(), bounds);
if (approximately_greater(top, bounds.fTop)) {
top = bounds.fTop;
first = index;
}
}
SkASSERT(first < SK_MaxS32);
#if DEBUG_SORT // || DEBUG_SWAP_TOP
sorted[first]->segment()->debugShowSort(__FUNCTION__, sorted, first, 0, 0);
#endif
if (onlySortable && !sortable) {
unsortable = true;
return NULL;
}
// skip edges that have already been processed
firstT = -1;
firstT = first - 1;
Segment* leftSegment;
do {
const Angle* angle = sorted[++firstT];
if (++firstT == count) {
firstT = 0;
}
const Angle* angle = sorted[firstT];
SkASSERT(!onlySortable || !angle->unsortable());
leftSegment = angle->segment();
tIndex = angle->end();
endIndex = angle->start();
} while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
if (leftSegment->verb() >= SkPath::kQuad_Verb) {
bool bumpsUp = leftSegment->bumpsUp(tIndex, endIndex);
SkPoint xyE = leftSegment->xyAtT(endIndex);
SkPoint xyS = leftSegment->xyAtT(tIndex);
SkVector dxyE = leftSegment->dxdy(endIndex);
SkVector dxyS = leftSegment->dxdy(tIndex);
double cross = dxyE.cross(dxyS);
bool bumpCheck = bumpsUp && xyE.fY < xyS.fY && dxyE.fX < 0;
if (xyE == xyS){
SkDebugf("%s ignore loops\n", __FUNCTION__);
cross = 0;
}
if (!leftSegment->clockwise(tIndex, endIndex)) {
bool swap = leftSegment->verb() == SkPath::kQuad_Verb
|| (!leftSegment->monotonic_in_y(tIndex, endIndex)
&& !leftSegment->serpentine(tIndex, endIndex));
#if DEBUG_SWAP_TOP
SkDebugf("%s xyE=(%1.9g,%1.9g) xyS=(%1.9g,%1.9g)\n", __FUNCTION__,
xyE.fX, xyE.fY, xyS.fX, xyS.fY);
SkDebugf("%s dxyE=(%1.9g,%1.9g) dxyS=(%1.9g,%1.9g) cross=%1.9g bumpsUp=%s\n",
__FUNCTION__,
dxyE.fX, dxyE.fY, dxyS.fX, dxyS.fY, cross, bumpsUp ? "true" : "false");
if ((cross > 0) ^ bumpCheck) {
leftSegment->bumpsUp(tIndex, endIndex);
SkDebugf("%s cross bump disagree\n", __FUNCTION__);
}
SkDebugf("%s swap=%d serpentine=%d controls_contained_by_ends=%d\n", __FUNCTION__,
swap,
leftSegment->serpentine(tIndex, endIndex),
leftSegment->controls_contained_by_ends(tIndex, endIndex),
leftSegment->monotonic_in_y(tIndex, endIndex));
#endif
if (cross > 0 || bumpCheck) {
#if DEBUG_SWAP_TOP
SkDebugf("%s swap\n", __FUNCTION__);
#endif
SkTSwap(tIndex, endIndex);
if (swap) {
// FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
// sorted but merely the first not already processed (i.e., not done)
SkTSwap(tIndex, endIndex);
}
}
}
SkASSERT(!leftSegment->fTs[SkMin32(tIndex, endIndex)].fTiny);
@ -2967,7 +2983,7 @@ public:
// FIXME: not crazy about this
// when the intersections are performed, the other index is into an
// incomplete array. as the array grows, the indices become incorrect
// incomplete array. As the array grows, the indices become incorrect
// while the following fixes the indices up again, it isn't smart about
// skipping segments whose indices are already correct
// assuming we leave the code that wrote the index in the first place
@ -2980,7 +2996,7 @@ public:
int oCount = other->fTs.count();
for (int o = 0; o < oCount; ++o) {
Span& oSpan = other->fTs[o];
if (oT == oSpan.fT && this == oSpan.fOther) {
if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
iSpan.fOtherIndex = o;
break;
}
@ -3423,25 +3439,59 @@ the same winding is shared by both.
return &span;
}
bool bumpsUp(int tStart, int tEnd) const {
bool controls_contained_by_ends(int tStart, int tEnd) const {
if (fVerb != SkPath::kCubic_Verb) {
return false;
}
MAKE_CONST_CUBIC(aCubic, fPts);
Cubic dst;
sub_divide(aCubic, fTs[tStart].fT, fTs[tEnd].fT, dst);
return ::controls_contained_by_ends(dst);
}
// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
bool clockwise(int tStart, int tEnd) const {
SkASSERT(fVerb != SkPath::kLine_Verb);
SkPoint edge[4];
subDivide(tStart, tEnd, edge);
switch (fVerb) {
case SkPath::kLine_Verb:
SkASSERT(0); // shouldn't call in for lines
return true;
case SkPath::kQuad_Verb:
return approximately_greater(edge[0].fY, edge[1].fY)
&& approximately_lesser(edge[1].fY, edge[2].fY);
case SkPath::kCubic_Verb:
return (approximately_greater(edge[0].fY, edge[1].fY)
&& approximately_lesser(edge[1].fY, edge[3].fY))
|| (approximately_greater(edge[0].fY, edge[2].fY)
&& approximately_lesser(edge[2].fY, edge[3].fY));
default:
SkASSERT(0);
return false;
double sum = (edge[0].fX - edge[fVerb].fX) * (edge[0].fY + edge[fVerb].fY);
if (fVerb == SkPath::kCubic_Verb) {
SkScalar lesser = SkTMin(edge[0].fY, edge[3].fY);
if (edge[1].fY < lesser && edge[2].fY < lesser) {
_Line tangent1 = { {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} };
_Line tangent2 = { {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} };
if (testIntersect(tangent1, tangent2)) {
SkPoint topPt = CubicTop(fPts, fTs[tStart].fT, fTs[tEnd].fT);
sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
return sum <= 0;
}
}
}
for (int idx = 0; idx < fVerb; ++idx){
sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
}
return sum <= 0;
}
bool monotonic_in_y(int tStart, int tEnd) const {
if (fVerb != SkPath::kCubic_Verb) {
return false;
}
MAKE_CONST_CUBIC(aCubic, fPts);
Cubic dst;
sub_divide(aCubic, fTs[tStart].fT, fTs[tEnd].fT, dst);
return ::monotonic_in_y(dst);
}
bool serpentine(int tStart, int tEnd) const {
if (fVerb != SkPath::kCubic_Verb) {
return false;
}
MAKE_CONST_CUBIC(aCubic, fPts);
Cubic dst;
sub_divide(aCubic, fTs[tStart].fT, fTs[tEnd].fT, dst);
return ::serpentine(dst);
}
Span* verifyOneWinding(const char* funName, int tIndex) {
@ -3477,15 +3527,13 @@ the same winding is shared by both.
Span* span = &fTs[start];
if (start < end) {
#if DEBUG_UNSORTABLE
SkDebugf("%s start id=%d [%d] (%1.9g,%1.9g)\n", __FUNCTION__, fID, start,
xAtT(start), yAtT(start));
debugShowNewWinding(__FUNCTION__, *span, 0);
#endif
span->fUnsortableStart = true;
} else {
--span;
#if DEBUG_UNSORTABLE
SkDebugf("%s end id=%d [%d] (%1.9g,%1.9g) next:(%1.9g,%1.9g)\n", __FUNCTION__, fID,
start - 1, xAtT(start - 1), yAtT(start - 1), xAtT(start), yAtT(start));
debugShowNewWinding(__FUNCTION__, *span, 0);
#endif
span->fUnsortableEnd = true;
}
@ -3799,6 +3847,12 @@ the same winding is shared by both.
}
}
}
void subDivideBounds(int start, int end, Bounds& bounds) const {
SkPoint edge[4];
subDivide(start, end, edge);
(bounds.*setSegmentBounds[fVerb])(edge);
}
// OPTIMIZATION: mark as debugging only if used solely by tests
double t(int tIndex) const {
@ -4207,7 +4261,7 @@ the same winding is shared by both.
}
#endif
#if DEBUG_MARK_DONE
#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
void debugShowNewWinding(const char* fun, const Span& span, int winding) {
const SkPoint& pt = xyAtT(&span);
SkDebugf("%s id=%d", fun, fID);
@ -4255,7 +4309,7 @@ the same winding is shared by both.
}
#endif
#if DEBUG_SORT
#if DEBUG_SORT || DEBUG_SWAP_TOP
void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
const int contourWinding, const int oppContourWinding) const {
if (--gDebugSortCount < 0) {
@ -6491,7 +6545,7 @@ static void assemble(const PathWrapper& path, PathWrapper& simple) {
}
void simplifyx(const SkPath& path, SkPath& result) {
#if DEBUG_SORT
#if DEBUG_SORT || DEBUG_SWAP_TOP
gDebugSortCount = gDebugSortCountDefault;
#endif
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness

View File

@ -4253,12 +4253,369 @@ static void cubicOp36u() {
testShapeOp(path, pathB, kUnion_Op);
}
static void (*firstTest)() = cubicOp36u;
static void cubicOp37d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,6, 6,1, 4,3);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,6);
pathB.cubicTo(3,4, 1,0, 6,2);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
#if 1
// this fails to detect a cubic/cubic intersection
// the slight overlap is missed when the cubics are approximated by quadratics
// and the subsequent line/cubic intersection also (correctly) misses the intersection
// if the line/cubic was a matching line/approx.quadratic then the missing intersection
// could have been detected
static void cubicOp38d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(0,6, 3,2, 4,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(1,4, 1,0, 6,0);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
#endif
static void cubicOp39d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,3, 5,1, 4,3);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,5);
pathB.cubicTo(3,4, 1,0, 3,2);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp40d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,5, 3,2, 4,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(2,4, 1,0, 5,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp41i() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,6, 4,3, 6,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(3,4);
pathB.cubicTo(4,6, 1,0, 6,2);
pathB.close();
testShapeOp(path, pathB, kIntersect_Op);
}
static void cubicOp42d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,2, 6,5, 5,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(5,6);
pathB.cubicTo(4,5, 1,0, 2,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp43d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(1,2, 4,0, 3,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(1,3, 2,0, 2,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp44d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(3,6, 4,0, 3,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(2,3, 2,0, 6,3);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp45d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(2,4, 4,0, 3,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(2,3, 2,0, 4,2);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp46d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(3,5, 5,0, 4,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,5);
pathB.cubicTo(2,4, 2,0, 5,3);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp47d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,6, 6,2, 5,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,6);
pathB.cubicTo(4,5, 1,0, 6,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp48d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(2,3, 5,1, 3,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,5);
pathB.cubicTo(2,3, 2,0, 3,2);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp49d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(1,5, 3,2, 4,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(1,4, 2,0, 5,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp50d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,3);
path.cubicTo(1,6, 5,0, 5,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,5);
pathB.cubicTo(1,5, 3,0, 6,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp51d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,3);
path.cubicTo(1,2, 4,1, 6,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,4);
pathB.cubicTo(0,6, 3,0, 2,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp52d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(1,2, 5,4, 4,3);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(4,5);
pathB.cubicTo(3,4, 2,0, 2,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp53d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,3);
path.cubicTo(1,2, 5,3, 2,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(3,5);
pathB.cubicTo(1,2, 3,0, 2,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp54d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,4);
path.cubicTo(1,3, 5,4, 4,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(4,5);
pathB.cubicTo(2,4, 4,0, 3,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp55d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,5);
path.cubicTo(1,3, 3,2, 5,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(0,5, 5,0, 3,1);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp56d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,6, 5,0, 2,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,5);
pathB.cubicTo(1,2, 1,0, 6,2);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp57d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,5);
path.cubicTo(0,5, 5,4, 6,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(4,5);
pathB.cubicTo(4,6, 5,0, 5,0);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp58d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,5);
path.cubicTo(3,4, 6,5, 5,3);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(5,6);
pathB.cubicTo(3,5, 5,0, 4,3);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp59d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(5,6, 4,0, 4,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(1,4, 1,0, 6,5);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp60d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(4,6, 6,0, 5,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,6);
pathB.cubicTo(2,5, 2,0, 6,4);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void cubicOp61d() {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(1,2);
path.cubicTo(0,5, 3,2, 6,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(1,6, 2,1, 5,0);
pathB.close();
testShapeOp(path, pathB, kDifference_Op);
}
static void (*firstTest)() = cubicOp61d;
static struct {
void (*fun)();
const char* str;
} tests[] = {
TEST(cubicOp61d),
TEST(cubicOp60d),
TEST(cubicOp59d),
TEST(cubicOp58d),
TEST(cubicOp57d),
TEST(cubicOp56d),
TEST(cubicOp55d),
TEST(cubicOp54d),
TEST(cubicOp53d),
TEST(cubicOp52d),
TEST(cubicOp51d),
TEST(cubicOp50d),
TEST(cubicOp49d),
TEST(cubicOp48d),
TEST(cubicOp47d),
TEST(cubicOp46d),
TEST(cubicOp45d),
TEST(cubicOp44d),
TEST(cubicOp43d),
TEST(cubicOp42d),
TEST(cubicOp41i),
TEST(cubicOp40d),
TEST(cubicOp39d),
TEST(cubicOp38d),
TEST(cubicOp37d),
TEST(cubicOp36u),
TEST(cubicOp35d),
TEST(cubicOp34d),
@ -4655,18 +5012,21 @@ static struct {
void (*fun)();
const char* str;
} subTests[] = {
TEST(cubicOp43d),
TEST(quadOp9d),
TEST(cubicOp9d),
TEST(cubicOp1i),
TEST(cubicOp10d),
TEST(cubicOp11d),
TEST(cubicOp15d),
TEST(cubicOp18d),
TEST(cubicOp22d),
TEST(cubicOp23d),
TEST(cubicOp24d),
TEST(cubicOp18d),
TEST(cubicOp15d),
TEST(cubicOp14d),
TEST(cubicOp13d),
TEST(cubicOp11d),
TEST(cubicOp9d),
TEST(cubicOp8d),
TEST(cubicOp7d),
TEST(cubicOp6d),
TEST(cubicOp5d),
TEST(cubicOp28u),
TEST(cubicOp33i),
TEST(cubicOp36u),
TEST(cubicOp40d),
};
static const size_t subTestCount = sizeof(subTests) / sizeof(subTests[0]);
@ -4685,8 +5045,8 @@ void SimplifyNew_Test() {
#ifdef SK_DEBUG
gDebugMaxWindSum = 4;
gDebugMaxWindValue = 4;
size_t index;
#endif
size_t index;
if (runSubTestsFirst && firstSubTest) {
index = subTestCount - 1;
while (index > 0 && subTests[index].fun != firstSubTest) {
@ -4707,6 +5067,9 @@ void SimplifyNew_Test() {
while (index > 0 && tests[index].fun != firstTest) {
--index;
}
#if FORCE_RELEASE == 0
SkDebugf("<div id=\"%s\">\n", tests[index].str);
#endif
SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
(*tests[index].fun)();
}

View File

@ -1,12 +1,9 @@
/*
* SkAntiEdge.cpp
* core
*
* Created by Cary Clark on 5/6/11.
* Copyright 2011 __MyCompanyName__. All rights reserved.
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkAntiEdge.h"
#include "SkPoint.h"

View File

@ -1,12 +1,9 @@
/*
* SkAntiEdge.h
* core
*
* Created by Cary Clark on 5/6/11.
* Copyright 2011 __MyCompanyName__. All rights reserved.
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkAntiEdge_DEFINED
#define SkAntiEdge_DEFINED

File diff suppressed because it is too large Load Diff

View File

@ -3776,11 +3776,311 @@ path.addRect(4, 13, 13, 16, SkPath::kCCW_Direction);
pathB.close();
</div>
<div id="cubicOp37d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,6, 6,1, 4,3);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,6);
pathB.cubicTo(3,4, 1,0, 6,2);
pathB.close();
</div>
<div id="cubicOp38d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(0,6, 3,2, 4,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(1,4, 1,0, 6,0);
pathB.close();
</div>
<div id="cubicOp39d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,3, 5,1, 4,3);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,5);
pathB.cubicTo(3,4, 1,0, 3,2);
pathB.close();
</div>
<div id="cubicOp40d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,5, 3,2, 4,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(2,4, 1,0, 5,1);
pathB.close();
</div>
<div id="cubicOp41i">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,6, 4,3, 6,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(3,4);
pathB.cubicTo(4,6, 1,0, 6,2);
pathB.close();
</div>
<div id="cubicOp42d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,2, 6,5, 5,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(5,6);
pathB.cubicTo(4,5, 1,0, 2,1);
pathB.close();
</div>
<div id="cubicOp43d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(1,2, 4,0, 3,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(1,3, 2,0, 2,1);
pathB.close();
</div>
<div id="cubicOp44d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(3,6, 4,0, 3,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(2,3, 2,0, 6,3);
pathB.close();
</div>
<div id="cubicOp45d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(2,4, 4,0, 3,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(2,3, 2,0, 4,2);
pathB.close();
</div>
<div id="cubicOp46d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(3,5, 5,0, 4,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,5);
pathB.cubicTo(2,4, 2,0, 5,3);
pathB.close();
</div>
<div id="cubicOp47d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,6, 6,2, 5,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,6);
pathB.cubicTo(4,5, 1,0, 6,1);
pathB.close();
</div>
<div id="cubicOp48d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(2,3, 5,1, 3,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,5);
pathB.cubicTo(2,3, 2,0, 3,2);
pathB.close();
</div>
<div id="cubicOp49d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(1,5, 3,2, 4,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(1,4, 2,0, 5,1);
pathB.close();
</div>
<div id="cubicOp50d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,3);
path.cubicTo(1,6, 5,0, 5,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,5);
pathB.cubicTo(1,5, 3,0, 6,1);
pathB.close();
</div>
<div id="cubicOp51d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,3);
path.cubicTo(1,2, 4,1, 6,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,4);
pathB.cubicTo(0,6, 3,0, 2,1);
pathB.close();
</div>
<div id="cubicOp52d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(1,2, 5,4, 4,3);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(4,5);
pathB.cubicTo(3,4, 2,0, 2,1);
pathB.close();
</div>
<div id="cubicOp53d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,3);
path.cubicTo(1,2, 5,3, 2,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(3,5);
pathB.cubicTo(1,2, 3,0, 2,1);
pathB.close();
</div>
<div id="cubicOp54d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,4);
path.cubicTo(1,3, 5,4, 4,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(4,5);
pathB.cubicTo(2,4, 4,0, 3,1);
pathB.close();
</div>
<div id="cubicOp55d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,5);
path.cubicTo(1,3, 3,2, 5,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(0,5, 5,0, 3,1);
pathB.close();
</div>
<div id="cubicOp56d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,6, 5,0, 2,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,5);
pathB.cubicTo(1,2, 1,0, 6,2);
pathB.close();
</div>
<div id="cubicOp57d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,5);
path.cubicTo(0,5, 5,4, 6,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(4,5);
pathB.cubicTo(4,6, 5,0, 5,0);
pathB.close();
</div>
<div id="cubicOp58d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,5);
path.cubicTo(3,4, 6,5, 5,3);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(5,6);
pathB.cubicTo(3,5, 5,0, 4,3);
pathB.close();
</div>
<div id="cubicOp59d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(5,6, 4,0, 4,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(1,4, 1,0, 6,5);
pathB.close();
</div>
<div id="cubicOp60d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(4,6, 6,0, 5,2);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,6);
pathB.cubicTo(2,5, 2,0, 6,4);
pathB.close();
</div>
<div id="cubicOp61d">
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(1,2);
path.cubicTo(0,5, 3,2, 6,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,3);
pathB.cubicTo(1,6, 2,1, 5,0);
pathB.close();
</div>
</div>
<script type="text/javascript">
var testDivs = [
cubicOp61d,
cubicOp60d,
cubicOp59d,
cubicOp58d,
cubicOp57d,
cubicOp56d,
cubicOp55d,
cubicOp54d,
cubicOp53d,
cubicOp52d,
cubicOp51d,
cubicOp50d,
cubicOp49d,
cubicOp48d,
cubicOp47d,
cubicOp46d,
cubicOp45d,
cubicOp44d,
cubicOp43d,
cubicOp42d,
cubicOp41i,
cubicOp40d,
cubicOp39d,
cubicOp38d,
cubicOp37d,
cubicOp36u,
cubicOp35d,
cubicOp34d,

View File

@ -33,6 +33,7 @@
'../experimental/Intersection/CubicToQuadratics.cpp',
'../experimental/Intersection/CubicToQuadratics_Test.cpp',
'../experimental/Intersection/CubicUtilities.cpp',
'../experimental/Intersection/CubicUtilities_Test.cpp',
'../experimental/Intersection/DataTypes.cpp',
'../experimental/Intersection/EdgeMain.cpp',
'../experimental/Intersection/EdgeWalker.cpp',
@ -63,7 +64,6 @@
'../experimental/Intersection/QuadraticBezierClip_Test.cpp',
'../experimental/Intersection/QuadraticBounds.cpp',
'../experimental/Intersection/QuadraticImplicit.cpp',
'../experimental/Intersection/QuadraticIntersection.cpp',
'../experimental/Intersection/QuadraticIntersection_Test.cpp',
'../experimental/Intersection/QuadraticIntersection_TestData.cpp',
'../experimental/Intersection/QuadraticLineSegments.cpp',