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:
parent
dcf9c19d38
commit
1304bb25aa
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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]));
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
28
experimental/Intersection/CubicUtilities_Test.cpp
Normal file
28
experimental/Intersection/CubicUtilities_Test.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -1,8 +0,0 @@
|
||||
/*
|
||||
* LogoPlay.h
|
||||
* shapeops_edge
|
||||
*
|
||||
* Created by Cary Clark on 2/6/13.
|
||||
* Copyright 2013 __MyCompanyName__. All rights reserved.
|
||||
*
|
||||
*/
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -3800,6 +3848,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 {
|
||||
return fTs[tIndex].fT;
|
||||
@ -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
|
||||
|
@ -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)();
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
@ -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,
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user