Add test for quadratic Wang's formula

Just verify that the max error on any of the intervals after splitting
is less than the specified tolerance. Looks like we're running into
some FP precision issues with large coord values, so I'm limiting the
max multiplier to 2^15 in this test.

Bug: skia:10419
Change-Id: I39e76dca5f77389833ba3c94930e6b67c2b1bc97
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/340116
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Tyler Denniston <tdenniston@google.com>
This commit is contained in:
Tyler Denniston 2020-12-02 14:22:12 -05:00 committed by Skia Commit-Bot
parent 3c161467f0
commit 4be95c541c

View File

@ -55,10 +55,11 @@ static void for_random_matrices(SkRandom* rand, std::function<void(const SkMatri
}
static void for_random_beziers(int numPoints, SkRandom* rand,
std::function<void(const SkPoint[])> f) {
std::function<void(const SkPoint[])> f,
int maxExponent = 30) {
SkASSERT(numPoints <= 4);
SkPoint pts[4];
for (int i = -10; i <= 30; ++i) {
for (int i = -10; i <= maxExponent; ++i) {
for (int j = 0; j < numPoints; ++j) {
pts[j].set(std::ldexp(1 + rand->nextF(), i), std::ldexp(1 + rand->nextF(), i));
}
@ -295,3 +296,54 @@ DEF_TEST(WangsFormula_worst_case_cubic, r) {
});
}
}
// Ensure Wang's formula for quads produces max error within tolerance.
DEF_TEST(WangsFormula_quad_within_tol, r) {
// Wang's formula and the quad math starts to lose precision with very large
// coordinate values, so limit the magnitude a bit to prevent test failures
// due to loss of precision.
constexpr int maxExponent = 15;
SkRandom rand;
for_random_beziers(3, &rand, [&r](const SkPoint pts[]) {
const int nsegs = static_cast<int>(
std::ceil(wangs_formula_quadratic_reference_impl(kIntolerance, pts)));
const float tdelta = 1.f / nsegs;
for (int j = 0; j < nsegs; ++j) {
const float tmin = j * tdelta, tmax = (j + 1) * tdelta;
// Get section of quad in [tmin,tmax]
const SkPoint* sectionPts;
SkPoint tmp0[5];
SkPoint tmp1[5];
if (tmin == 0) {
if (tmax == 1) {
sectionPts = pts;
} else {
SkChopQuadAt(pts, tmp0, tmax);
sectionPts = tmp0;
}
} else {
SkChopQuadAt(pts, tmp0, tmin);
if (tmax == 1) {
sectionPts = tmp0 + 2;
} else {
SkChopQuadAt(tmp0 + 2, tmp1, (tmax - tmin) / (1 - tmin));
sectionPts = tmp1;
}
}
// For quads, max distance from baseline is always at t=0.5.
SkPoint p;
p = SkEvalQuadAt(sectionPts, 0.5f);
// Get distance of p to baseline
const SkPoint n = {sectionPts[2].fY - sectionPts[0].fY,
sectionPts[0].fX - sectionPts[2].fX};
const float d = std::abs((p - sectionPts[0]).dot(n)) / n.length();
// Check distance is within specified tolerance
REPORTER_ASSERT(r, d <= (1.f / kIntolerance) + SK_ScalarNearlyZero);
}
}, maxExponent);
}