1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkCubicMap.h" 9cb93a386Sopenharmony_ci#include "include/core/SkPoint.h" 10cb93a386Sopenharmony_ci#include "include/core/SkScalar.h" 11cb93a386Sopenharmony_ci#include "include/core/SkTypes.h" 12cb93a386Sopenharmony_ci#include "include/private/SkNx.h" 13cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h" 14cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsCubic.h" 15cb93a386Sopenharmony_ci#include "tests/Test.h" 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_cistatic float accurate_t(float A, float B, float C, float D) { 18cb93a386Sopenharmony_ci double roots[3]; 19cb93a386Sopenharmony_ci SkDEBUGCODE(int count =) SkDCubic::RootsValidT(A, B, C, D, roots); 20cb93a386Sopenharmony_ci SkASSERT(count == 1); 21cb93a386Sopenharmony_ci return (float)roots[0]; 22cb93a386Sopenharmony_ci} 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_cistatic float accurate_solve(SkPoint p1, SkPoint p2, SkScalar x) { 25cb93a386Sopenharmony_ci SkPoint array[] = { {0, 0}, p1, p2, {1,1} }; 26cb93a386Sopenharmony_ci SkCubicCoeff coeff(array); 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_ci float t = accurate_t(coeff.fA[0], coeff.fB[0], coeff.fC[0], coeff.fD[0] - x); 29cb93a386Sopenharmony_ci SkASSERT(t >= 0 && t <= 1); 30cb93a386Sopenharmony_ci float y = coeff.eval(t)[1]; 31cb93a386Sopenharmony_ci SkASSERT(y >= 0 && y <= 1.0001f); 32cb93a386Sopenharmony_ci return y; 33cb93a386Sopenharmony_ci} 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_cistatic bool nearly_le(SkScalar a, SkScalar b) { 36cb93a386Sopenharmony_ci return a <= b || SkScalarNearlyZero(a - b); 37cb93a386Sopenharmony_ci} 38cb93a386Sopenharmony_ci 39cb93a386Sopenharmony_cistatic void exercise_cubicmap(SkPoint p1, SkPoint p2, skiatest::Reporter* reporter) { 40cb93a386Sopenharmony_ci const SkScalar MAX_SOLVER_ERR = 0.008f; // found by running w/ current impl 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_ci SkCubicMap cmap(p1, p2); 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_ci SkScalar prev_y = 0; 45cb93a386Sopenharmony_ci SkScalar dx = 1.0f / 512; 46cb93a386Sopenharmony_ci for (SkScalar x = dx; x < 1; x += dx) { 47cb93a386Sopenharmony_ci SkScalar y = cmap.computeYFromX(x); 48cb93a386Sopenharmony_ci // are we valid and (mostly) monotonic? 49cb93a386Sopenharmony_ci if (!nearly_le(prev_y, y)) { 50cb93a386Sopenharmony_ci cmap.computeYFromX(x); 51cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, false); 52cb93a386Sopenharmony_ci } 53cb93a386Sopenharmony_ci prev_y = y; 54cb93a386Sopenharmony_ci 55cb93a386Sopenharmony_ci // are we close to the "correct" answer? 56cb93a386Sopenharmony_ci SkScalar yy = accurate_solve(p1, p2, x); 57cb93a386Sopenharmony_ci SkScalar diff = SkScalarAbs(yy - y); 58cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, diff < MAX_SOLVER_ERR); 59cb93a386Sopenharmony_ci } 60cb93a386Sopenharmony_ci} 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ciDEF_TEST(CubicMap, r) { 63cb93a386Sopenharmony_ci const SkScalar values[] = { 64cb93a386Sopenharmony_ci 0, 1, 0.5f, 0.0000001f, 0.999999f, 65cb93a386Sopenharmony_ci }; 66cb93a386Sopenharmony_ci 67cb93a386Sopenharmony_ci for (SkScalar x0 : values) { 68cb93a386Sopenharmony_ci for (SkScalar y0 : values) { 69cb93a386Sopenharmony_ci for (SkScalar x1 : values) { 70cb93a386Sopenharmony_ci for (SkScalar y1 : values) { 71cb93a386Sopenharmony_ci exercise_cubicmap({ x0, y0 }, { x1, y1 }, r); 72cb93a386Sopenharmony_ci } 73cb93a386Sopenharmony_ci } 74cb93a386Sopenharmony_ci } 75cb93a386Sopenharmony_ci } 76cb93a386Sopenharmony_ci} 77