aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/PathOpsCubicQuadIntersectionTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/PathOpsCubicQuadIntersectionTest.cpp')
-rw-r--r--tests/PathOpsCubicQuadIntersectionTest.cpp284
1 files changed, 246 insertions, 38 deletions
diff --git a/tests/PathOpsCubicQuadIntersectionTest.cpp b/tests/PathOpsCubicQuadIntersectionTest.cpp
index 0144bade22..3827ebd8b5 100644
--- a/tests/PathOpsCubicQuadIntersectionTest.cpp
+++ b/tests/PathOpsCubicQuadIntersectionTest.cpp
@@ -8,6 +8,7 @@
#include "SkIntersections.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsQuad.h"
+#include "SkRandom.h"
#include "SkReduceOrder.h"
#include "Test.h"
@@ -17,6 +18,12 @@ static struct lineCubic {
int answerCount;
SkDPoint answers[2];
} quadCubicTests[] = {
+#if 0 // FIXME : this should not fail (root problem behind skpcarrot_is24 )
+ {{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
+ {{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}, 1,
+ {{1019.421, 662.449}}},
+#endif
+
{{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}},
{{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}, 2,
{{778, 14089}, {776.82855609581270,14091.828250841330}}},
@@ -48,50 +55,251 @@ static struct lineCubic {
{{10,234}, {0,0}}},
};
-static const size_t quadCubicTests_count = SK_ARRAY_COUNT(quadCubicTests);
+static const int quadCubicTests_count = (int) SK_ARRAY_COUNT(quadCubicTests);
-DEF_TEST(PathOpsCubicQuadIntersection, reporter) {
- for (size_t index = 0; index < quadCubicTests_count; ++index) {
- int iIndex = static_cast<int>(index);
- const SkDCubic& cubic = quadCubicTests[index].cubic;
- SkASSERT(ValidCubic(cubic));
- const SkDQuad& quad = quadCubicTests[index].quad;
- SkASSERT(ValidQuad(quad));
- SkReduceOrder reduce1;
- SkReduceOrder reduce2;
- int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics);
- int order2 = reduce2.reduce(quad);
- if (order1 != 4) {
- SkDebugf("[%d] cubic order=%d\n", iIndex, order1);
- REPORTER_ASSERT(reporter, 0);
+static void cubicQuadIntersection(skiatest::Reporter* reporter, int index) {
+ int iIndex = static_cast<int>(index);
+ const SkDCubic& cubic = quadCubicTests[index].cubic;
+ SkASSERT(ValidCubic(cubic));
+ const SkDQuad& quad = quadCubicTests[index].quad;
+ SkASSERT(ValidQuad(quad));
+ SkReduceOrder reduce1;
+ SkReduceOrder reduce2;
+ int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics);
+ int order2 = reduce2.reduce(quad);
+ if (order1 != 4) {
+ SkDebugf("[%d] cubic order=%d\n", iIndex, order1);
+ REPORTER_ASSERT(reporter, 0);
+ }
+ if (order2 != 3) {
+ SkDebugf("[%d] quad order=%d\n", iIndex, order2);
+ REPORTER_ASSERT(reporter, 0);
+ }
+ SkIntersections i;
+ int roots = i.intersect(cubic, quad);
+ SkASSERT(roots == quadCubicTests[index].answerCount);
+ for (int pt = 0; pt < roots; ++pt) {
+ double tt1 = i[0][pt];
+ SkDPoint xy1 = cubic.ptAtT(tt1);
+ double tt2 = i[1][pt];
+ SkDPoint xy2 = quad.ptAtT(tt2);
+ if (!xy1.approximatelyEqual(xy2)) {
+ SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+ __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
}
- if (order2 != 3) {
- SkDebugf("[%d] quad order=%d\n", iIndex, order2);
- REPORTER_ASSERT(reporter, 0);
+ REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
+ bool found = false;
+ for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) {
+ found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1);
}
+ if (!found) {
+ SkDebugf("%s [%d,%d] xy1=(%g,%g) != \n",
+ __FUNCTION__, iIndex, pt, xy1.fX, xy1.fY);
+ }
+ REPORTER_ASSERT(reporter, found);
+ }
+ reporter->bumpTestCount();
+}
+
+DEF_TEST(PathOpsCubicQuadIntersection, reporter) {
+ for (int index = 0; index < quadCubicTests_count; ++index) {
+ cubicQuadIntersection(reporter, index);
+ reporter->bumpTestCount();
+ }
+}
+
+DEF_TEST(PathOpsCubicQuadIntersectionOneOff, reporter) {
+ cubicQuadIntersection(reporter, 0);
+}
+
+static bool gPathOpCubicQuadSlopVerbose = false;
+static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
+
+// determine that slop required after quad/quad finds a candidate intersection
+// use the cross of the tangents plus the distance from 1 or 0 as knobs
+DEF_TEST(PathOpsCubicQuadSlop, reporter) {
+ // create a random non-selfintersecting cubic
+ // break it into quadratics
+ // offset the quadratic, measuring the slop required to find the intersection
+ if (!gPathOpCubicQuadSlopVerbose) { // takes a while to run -- so exclude it by default
+ return;
+ }
+ int results[101];
+ sk_bzero(results, sizeof(results));
+ double minCross[101];
+ sk_bzero(minCross, sizeof(minCross));
+ double maxCross[101];
+ sk_bzero(maxCross, sizeof(maxCross));
+ double sumCross[101];
+ sk_bzero(sumCross, sizeof(sumCross));
+ int foundOne = 0;
+ int slopCount = 1;
+ SkRandom ran;
+ for (int index = 0; index < 10000000; ++index) {
+ if (index % 1000 == 999) SkDebugf(".");
+ SkDCubic cubic = {{
+ {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+ {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+ {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+ {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}
+ }};
SkIntersections i;
- int roots = i.intersect(cubic, quad);
- SkASSERT(roots == quadCubicTests[index].answerCount);
- for (int pt = 0; pt < roots; ++pt) {
- double tt1 = i[0][pt];
- SkDPoint xy1 = cubic.ptAtT(tt1);
- double tt2 = i[1][pt];
- SkDPoint xy2 = quad.ptAtT(tt2);
- if (!xy1.approximatelyEqual(xy2)) {
- SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
- __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
- }
- REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
- bool found = false;
- for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) {
- found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1);
+ if (i.intersect(cubic)) {
+ continue;
+ }
+ SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts;
+ cubic.toQuadraticTs(cubic.calcPrecision(), &ts);
+ double tStart = 0;
+ int tsCount = ts.count();
+ for (int i1 = 0; i1 <= tsCount; ++i1) {
+ const double tEnd = i1 < tsCount ? ts[i1] : 1;
+ SkDCubic part = cubic.subDivide(tStart, tEnd);
+ SkDQuad quad = part.toQuad();
+ SkReduceOrder reducer;
+ int order = reducer.reduce(quad);
+ if (order != 3) {
+ continue;
}
- if (!found) {
- SkDebugf("%s [%d,%d] xy1=(%g,%g) != \n",
- __FUNCTION__, iIndex, pt, xy1.fX, xy1.fY);
+ for (int i2 = 0; i2 < 100; ++i2) {
+ SkDPoint endDisplacement = {ran.nextRangeF(-100, 100), ran.nextRangeF(-100, 100)};
+ SkDQuad nearby = {{
+ {quad[0].fX + endDisplacement.fX, quad[0].fY + endDisplacement.fY},
+ {quad[1].fX + ran.nextRangeF(-100, 100), quad[1].fY + ran.nextRangeF(-100, 100)},
+ {quad[2].fX - endDisplacement.fX, quad[2].fY - endDisplacement.fY}
+ }};
+ order = reducer.reduce(nearby);
+ if (order != 3) {
+ continue;
+ }
+ SkIntersections locals;
+ locals.allowNear(false);
+ locals.intersect(quad, nearby);
+ if (locals.used() != 1) {
+ continue;
+ }
+ // brute force find actual intersection
+ SkDLine cubicLine = {{ {0, 0}, {cubic[0].fX, cubic[0].fY } }};
+ SkIntersections liner;
+ int i3;
+ int found = -1;
+ int foundErr = true;
+ for (i3 = 1; i3 <= 1000; ++i3) {
+ cubicLine[0] = cubicLine[1];
+ cubicLine[1] = cubic.ptAtT(i3 / 1000.);
+ liner.reset();
+ liner.allowNear(false);
+ liner.intersect(nearby, cubicLine);
+ if (liner.used() == 0) {
+ continue;
+ }
+ if (liner.used() > 1) {
+ foundErr = true;
+ break;
+ }
+ if (found > 0) {
+ foundErr = true;
+ break;
+ }
+ foundErr = false;
+ found = i3;
+ }
+ if (foundErr) {
+ continue;
+ }
+ SkDVector dist = liner.pt(0) - locals.pt(0);
+ SkDVector qV = nearby.dxdyAtT(locals[0][0]);
+ double cubicT = (found - 1 + liner[1][0]) / 1000.;
+ SkDVector cV = cubic.dxdyAtT(cubicT);
+ double qxc = qV.crossCheck(cV);
+ double qvLen = qV.length();
+ double cvLen = cV.length();
+ double maxLen = SkTMax(qvLen, cvLen);
+ qxc /= maxLen;
+ double quadT = tStart + (tEnd - tStart) * locals[0][0];
+ double diffT = fabs(cubicT - quadT);
+ int diffIdx = (int) (diffT * 100);
+ results[diffIdx]++;
+ double absQxc = fabs(qxc);
+ if (sumCross[diffIdx] == 0) {
+ minCross[diffIdx] = maxCross[diffIdx] = sumCross[diffIdx] = absQxc;
+ } else {
+ minCross[diffIdx] = SkTMin(minCross[diffIdx], absQxc);
+ maxCross[diffIdx] = SkTMax(maxCross[diffIdx], absQxc);
+ sumCross[diffIdx] += absQxc;
+ }
+ if (diffIdx >= 20) {
+#if 01
+ SkDebugf("cubic={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
+ " quad={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
+ " {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
+ " qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
+ cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
+ cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
+ nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
+ nearby[2].fX, nearby[2].fY,
+ liner.pt(0).fX, liner.pt(0).fY,
+ locals.pt(0).fX, locals.pt(0).fY, quadT, cubicT, dist.length(), qxc);
+#else
+ SkDebugf("qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
+ quadT, cubicT, dist.length(), qxc);
+ SkDebugf("<div id=\"slop%d\">\n", ++slopCount);
+ SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
+ "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
+ "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n",
+ cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
+ cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
+ nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
+ nearby[2].fX, nearby[2].fY,
+ liner.pt(0).fX, liner.pt(0).fY,
+ locals.pt(0).fX, locals.pt(0).fY);
+ SkDebugf("</div>\n\n");
+#endif
+ }
+ ++foundOne;
}
- REPORTER_ASSERT(reporter, found);
+ tStart = tEnd;
}
- reporter->bumpTestCount();
+ if (++foundOne >= 100000) {
+ break;
+ }
+ }
+#if 01
+ SkDebugf("slopCount=%d\n", slopCount);
+ int max = 100;
+ while (results[max] == 0) {
+ --max;
+ }
+ for (int i = 0; i <= max; ++i) {
+ if (i > 0 && i % 10 == 0) {
+ SkDebugf("\n");
+ }
+ SkDebugf("%d ", results[i]);
+ }
+ SkDebugf("min\n");
+ for (int i = 0; i <= max; ++i) {
+ if (i > 0 && i % 10 == 0) {
+ SkDebugf("\n");
+ }
+ SkDebugf("%1.9g ", minCross[i]);
+ }
+ SkDebugf("max\n");
+ for (int i = 0; i <= max; ++i) {
+ if (i > 0 && i % 10 == 0) {
+ SkDebugf("\n");
+ }
+ SkDebugf("%1.9g ", maxCross[i]);
+ }
+ SkDebugf("avg\n");
+ for (int i = 0; i <= max; ++i) {
+ if (i > 0 && i % 10 == 0) {
+ SkDebugf("\n");
+ }
+ SkDebugf("%1.9g ", sumCross[i] / results[i]);
+ }
+#else
+ for (int i = 1; i < slopCount; ++i) {
+ SkDebugf(" slop%d,\n", i);
}
+#endif
+ SkDebugf("\n");
}