29b2563afb
The addT() function is a workhorse of pathops. Make it simpler, removing branches and parameters. Separate addOpp() into const and modify parts. Add more debugging that asserts if the function fails and the data is not extreme (e.g., fuzzer generated). TBR=reed@google.com GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2273293004 Review-Url: https://codereview.chromium.org/2273293004
528 lines
22 KiB
C++
528 lines
22 KiB
C++
/*
|
|
* Copyright 2013 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
#include "PathOpsTestCommon.h"
|
|
#include "SkIntersections.h"
|
|
#include "SkOpContour.h"
|
|
#include "SkOpSegment.h"
|
|
#include "SkRandom.h"
|
|
#include "SkTSort.h"
|
|
#include "Test.h"
|
|
|
|
static bool gDisableAngleTests = true;
|
|
|
|
static float next(float f)
|
|
{
|
|
int fBits = SkFloatAs2sCompliment(f);
|
|
++fBits;
|
|
float fNext = Sk2sComplimentAsFloat(fBits);
|
|
return fNext;
|
|
}
|
|
|
|
static float prev(float f)
|
|
{
|
|
int fBits = SkFloatAs2sCompliment(f);
|
|
--fBits;
|
|
float fNext = Sk2sComplimentAsFloat(fBits);
|
|
return fNext;
|
|
}
|
|
|
|
DEF_TEST(PathOpsAngleFindCrossEpsilon, reporter) {
|
|
if (gDisableAngleTests) {
|
|
return;
|
|
}
|
|
SkRandom ran;
|
|
int maxEpsilon = 0;
|
|
for (int index = 0; index < 10000000; ++index) {
|
|
SkDLine line = {{{0, 0}, {ran.nextRangeF(0.0001f, 1000), ran.nextRangeF(0.0001f, 1000)}}};
|
|
for (int inner = 0; inner < 10; ++inner) {
|
|
float t = ran.nextRangeF(0.0001f, 1);
|
|
SkDPoint dPt = line.ptAtT(t);
|
|
SkPoint pt = dPt.asSkPoint();
|
|
float xs[3] = { prev(pt.fX), pt.fX, next(pt.fX) };
|
|
float ys[3] = { prev(pt.fY), pt.fY, next(pt.fY) };
|
|
for (int xIdx = 0; xIdx < 3; ++xIdx) {
|
|
for (int yIdx = 0; yIdx < 3; ++yIdx) {
|
|
SkPoint test = { xs[xIdx], ys[yIdx] };
|
|
float p1 = SkDoubleToScalar(line[1].fX * test.fY);
|
|
float p2 = SkDoubleToScalar(line[1].fY * test.fX);
|
|
int p1Bits = SkFloatAs2sCompliment(p1);
|
|
int p2Bits = SkFloatAs2sCompliment(p2);
|
|
int epsilon = SkTAbs(p1Bits - p2Bits);
|
|
if (maxEpsilon < epsilon) {
|
|
SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g pt={%1.7g, %1.7g}"
|
|
" epsilon=%d\n",
|
|
line[1].fX, line[1].fY, t, test.fX, test.fY, epsilon);
|
|
maxEpsilon = epsilon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DEF_TEST(PathOpsAngleFindQuadEpsilon, reporter) {
|
|
if (gDisableAngleTests) {
|
|
return;
|
|
}
|
|
SkRandom ran;
|
|
int maxEpsilon = 0;
|
|
double maxAngle = 0;
|
|
for (int index = 0; index < 100000; ++index) {
|
|
SkDLine line = {{{0, 0}, {ran.nextRangeF(0.0001f, 1000), ran.nextRangeF(0.0001f, 1000)}}};
|
|
float t = ran.nextRangeF(0.0001f, 1);
|
|
SkDPoint dPt = line.ptAtT(t);
|
|
float t2 = ran.nextRangeF(0.0001f, 1);
|
|
SkDPoint qPt = line.ptAtT(t2);
|
|
float t3 = ran.nextRangeF(0.0001f, 1);
|
|
SkDPoint qPt2 = line.ptAtT(t3);
|
|
qPt.fX += qPt2.fY;
|
|
qPt.fY -= qPt2.fX;
|
|
SkDQuad quad = {{line[0], dPt, qPt}};
|
|
// binary search for maximum movement of quad[1] towards test that still has 1 intersection
|
|
double moveT = 0.5f;
|
|
double deltaT = moveT / 2;
|
|
SkDPoint last;
|
|
do {
|
|
last = quad[1];
|
|
quad[1].fX = dPt.fX - line[1].fY * moveT;
|
|
quad[1].fY = dPt.fY + line[1].fX * moveT;
|
|
SkIntersections i;
|
|
i.intersect(quad, line);
|
|
REPORTER_ASSERT(reporter, i.used() > 0);
|
|
if (i.used() == 1) {
|
|
moveT += deltaT;
|
|
} else {
|
|
moveT -= deltaT;
|
|
}
|
|
deltaT /= 2;
|
|
} while (last.asSkPoint() != quad[1].asSkPoint());
|
|
float p1 = SkDoubleToScalar(line[1].fX * last.fY);
|
|
float p2 = SkDoubleToScalar(line[1].fY * last.fX);
|
|
int p1Bits = SkFloatAs2sCompliment(p1);
|
|
int p2Bits = SkFloatAs2sCompliment(p2);
|
|
int epsilon = SkTAbs(p1Bits - p2Bits);
|
|
if (maxEpsilon < epsilon) {
|
|
SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g"
|
|
" pt={%1.7g, %1.7g} epsilon=%d\n",
|
|
line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, epsilon);
|
|
maxEpsilon = epsilon;
|
|
}
|
|
double a1 = atan2(line[1].fY, line[1].fX);
|
|
double a2 = atan2(last.fY, last.fX);
|
|
double angle = fabs(a1 - a2);
|
|
if (maxAngle < angle) {
|
|
SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g"
|
|
" pt={%1.7g, %1.7g} angle=%1.7g\n",
|
|
line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, angle);
|
|
maxAngle = angle;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int find_slop(double x, double y, double rx, double ry) {
|
|
int slopBits = 0;
|
|
bool less1, less2;
|
|
double absX = fabs(x);
|
|
double absY = fabs(y);
|
|
double length = absX < absY ? absX / 2 + absY : absX + absY / 2;
|
|
int exponent;
|
|
(void) frexp(length, &exponent);
|
|
double epsilon = ldexp(FLT_EPSILON, exponent);
|
|
do {
|
|
// get the length as the larger plus half the smaller (both same signs)
|
|
// find the ulps of the length
|
|
// compute the offsets from there
|
|
double xSlop = epsilon * slopBits;
|
|
double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _copysign ?
|
|
double x1 = x - xSlop;
|
|
double y1 = y + ySlop;
|
|
double x_ry1 = x1 * ry;
|
|
double rx_y1 = rx * y1;
|
|
less1 = x_ry1 < rx_y1;
|
|
double x2 = x + xSlop;
|
|
double y2 = y - ySlop;
|
|
double x_ry2 = x2 * ry;
|
|
double rx_y2 = rx * y2;
|
|
less2 = x_ry2 < rx_y2;
|
|
} while (less1 == less2 && ++slopBits);
|
|
return slopBits;
|
|
}
|
|
|
|
// from http://stackoverflow.com/questions/1427422/cheap-algorithm-to-find-measure-of-angle-between-vectors
|
|
static double diamond_angle(double y, double x)
|
|
{
|
|
if (y >= 0)
|
|
return (x >= 0 ? y/(x+y) : 1-x/(-x+y));
|
|
else
|
|
return (x < 0 ? 2-y/(-x-y) : 3+x/(x-y));
|
|
}
|
|
|
|
static const double slopTests[][4] = {
|
|
// x y rx ry
|
|
{-0.058554756452593892, -0.18804585843827226, -0.018568569646021160, -0.059615294434479438},
|
|
{-0.0013717412948608398, 0.0041152238845825195, -0.00045837944195925573, 0.0013753175735478074},
|
|
{-2.1033774145221198, -1.4046019261273715e-008, -0.70062688352066704, -1.2706324683777995e-008},
|
|
};
|
|
|
|
DEF_TEST(PathOpsAngleFindSlop, reporter) {
|
|
if (gDisableAngleTests) {
|
|
return;
|
|
}
|
|
for (int index = 0; index < (int) SK_ARRAY_COUNT(slopTests); ++index) {
|
|
const double* slopTest = slopTests[index];
|
|
double x = slopTest[0];
|
|
double y = slopTest[1];
|
|
double rx = slopTest[2];
|
|
double ry = slopTest[3];
|
|
SkDebugf("%s xy %d=%d\n", __FUNCTION__, index, find_slop(x, y, rx, ry));
|
|
SkDebugf("%s rxy %d=%d\n", __FUNCTION__, index, find_slop(rx, ry, x, y));
|
|
double angle = diamond_angle(y, x);
|
|
double rAngle = diamond_angle(ry, rx);
|
|
double diff = fabs(angle - rAngle);
|
|
SkDebugf("%s diamond xy=%1.9g rxy=%1.9g diff=%1.9g factor=%d\n", __FUNCTION__,
|
|
angle, rAngle, diff, (int) (diff / FLT_EPSILON));
|
|
}
|
|
}
|
|
|
|
class PathOpsAngleTester {
|
|
public:
|
|
static int After(SkOpAngle& lh, SkOpAngle& rh) {
|
|
return lh.after(&rh);
|
|
}
|
|
|
|
static int AllOnOneSide(SkOpAngle& lh, SkOpAngle& rh) {
|
|
return lh.allOnOneSide(&rh);
|
|
}
|
|
|
|
static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
|
|
return lh.convexHullOverlaps(&rh);
|
|
}
|
|
|
|
static int Orderable(SkOpAngle& lh, SkOpAngle& rh) {
|
|
return lh.orderable(&rh);
|
|
}
|
|
|
|
static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
|
|
return lh.endsIntersect(&rh);
|
|
}
|
|
|
|
static void SetNext(SkOpAngle& lh, SkOpAngle& rh) {
|
|
lh.fNext = &rh;
|
|
}
|
|
};
|
|
|
|
class PathOpsSegmentTester {
|
|
public:
|
|
static void DebugReset(SkOpSegment* segment) {
|
|
segment->debugReset();
|
|
}
|
|
};
|
|
|
|
struct CircleData {
|
|
const SkDCubic fPts;
|
|
const int fPtCount;
|
|
SkPoint fShortPts[4];
|
|
};
|
|
|
|
static CircleData circleDataSet[] = {
|
|
{ {{{313.0155029296875, 207.90290832519531}, {320.05078125, 227.58743286132812}}}, 2, {} },
|
|
{ {{{313.0155029296875, 207.90290832519531}, {313.98246891063195, 219.33615203830394},
|
|
{320.05078125, 227.58743286132812}}}, 3, {} },
|
|
};
|
|
|
|
static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
|
|
|
|
DEF_TEST(PathOpsAngleCircle, reporter) {
|
|
SkChunkAlloc allocator(4096);
|
|
SkOpContourHead contour;
|
|
SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
|
contour.init(&state, false, false);
|
|
for (int index = 0; index < circleDataSetSize; ++index) {
|
|
CircleData& data = circleDataSet[index];
|
|
for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
|
|
data.fShortPts[idx2] = data.fPts.fPts[idx2].asSkPoint();
|
|
}
|
|
switch (data.fPtCount) {
|
|
case 2:
|
|
contour.addLine(data.fShortPts);
|
|
break;
|
|
case 3:
|
|
contour.addQuad(data.fShortPts);
|
|
break;
|
|
case 4:
|
|
contour.addCubic(data.fShortPts);
|
|
break;
|
|
}
|
|
}
|
|
SkOpSegment* first = contour.first();
|
|
first->debugAddAngle(0, 1);
|
|
SkOpSegment* next = first->next();
|
|
next->debugAddAngle(0, 1);
|
|
PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle());
|
|
}
|
|
|
|
struct IntersectData {
|
|
const SkDCubic fPts;
|
|
const int fPtCount;
|
|
double fTStart;
|
|
double fTEnd;
|
|
SkPoint fShortPts[4];
|
|
};
|
|
|
|
static IntersectData intersectDataSet1[] = {
|
|
{ {{{322.935669,231.030273}, {312.832214,220.393295}, {312.832214,203.454178}}}, 3,
|
|
0.865309956, 0.154740299, {} },
|
|
{ {{{322.12738,233.397751}, {295.718353,159.505829}}}, 2,
|
|
0.345028807, 0.0786326511, {} },
|
|
{ {{{322.935669,231.030273}, {312.832214,220.393295}, {312.832214,203.454178}}}, 3,
|
|
0.865309956, 1, {} },
|
|
{ {{{322.12738,233.397751}, {295.718353,159.505829}}}, 2,
|
|
0.345028807, 1, {} },
|
|
};
|
|
|
|
static IntersectData intersectDataSet2[] = {
|
|
{ {{{364.390686,157.898193}, {375.281769,136.674606}, {396.039917,136.674606}}}, 3,
|
|
0.578520747, 1, {} },
|
|
{ {{{364.390686,157.898193}, {375.281769,136.674606}, {396.039917,136.674606}}}, 3,
|
|
0.578520747, 0.536512973, {} },
|
|
{ {{{366.608826,151.196014}, {378.803101,136.674606}, {398.164948,136.674606}}}, 3,
|
|
0.490456543, 1, {} },
|
|
};
|
|
|
|
static IntersectData intersectDataSet3[] = {
|
|
{ {{{2.000000,0.000000}, {1.33333333,0.66666667}}}, 2, 1, 0, {} },
|
|
{ {{{1.33333333,0.66666667}, {0.000000,2.000000}}}, 2, 0, 0.25, {} },
|
|
{ {{{2.000000,2.000000}, {1.33333333,0.66666667}}}, 2, 1, 0, {} },
|
|
};
|
|
|
|
static IntersectData intersectDataSet4[] = {
|
|
{ {{{1.3333333,0.6666667}, {0.000,2.000}}}, 2, 0.250000006, 0, {} },
|
|
{ {{{1.000,0.000}, {1.000,1.000}}}, 2, 1, 0, {} },
|
|
{ {{{1.000,1.000}, {0.000,0.000}}}, 2, 0, 1, {} },
|
|
};
|
|
|
|
static IntersectData intersectDataSet5[] = {
|
|
{ {{{0.000,0.000}, {1.000,0.000}, {1.000,1.000}}}, 3, 1, 0.666666667, {} },
|
|
{ {{{0.000,0.000}, {2.000,1.000}, {0.000,2.000}}}, 3, 0.5, 1, {} },
|
|
{ {{{0.000,0.000}, {2.000,1.000}, {0.000,2.000}}}, 3, 0.5, 0, {} },
|
|
};
|
|
|
|
static IntersectData intersectDataSet6[] = { // pathops_visualizer.htm:3658
|
|
{ {{{0.000,1.000}, {3.000,4.000}, {1.000,0.000}, {3.000,0.000}}}, 4, 0.0925339054, 0, {} }, // pathops_visualizer.htm:3616
|
|
{ {{{0.000,1.000}, {0.000,3.000}, {1.000,0.000}, {4.000,3.000}}}, 4, 0.453872386, 0, {} }, // pathops_visualizer.htm:3616
|
|
{ {{{0.000,1.000}, {3.000,4.000}, {1.000,0.000}, {3.000,0.000}}}, 4, 0.0925339054, 0.417096368, {} }, // pathops_visualizer.htm:3616
|
|
};
|
|
|
|
static IntersectData intersectDataSet7[] = { // pathops_visualizer.htm:3748
|
|
{ {{{2.000,1.000}, {0.000,1.000}}}, 2, 0.5, 0, {} }, // pathops_visualizer.htm:3706
|
|
{ {{{2.000,0.000}, {0.000,2.000}}}, 2, 0.5, 1, {} }, // pathops_visualizer.htm:3706
|
|
{ {{{0.000,1.000}, {0.000,2.000}, {2.000,0.000}, {2.000,1.000}}}, 4, 0.5, 1, {} }, // pathops_visualizer.htm:3706
|
|
}; //
|
|
|
|
static IntersectData intersectDataSet8[] = { // pathops_visualizer.htm:4194
|
|
{ {{{0.000,1.000}, {2.000,3.000}, {5.000,1.000}, {4.000,3.000}}}, 4, 0.311007457, 0.285714286, {} }, // pathops_visualizer.htm:4152
|
|
{ {{{1.000,5.000}, {3.000,4.000}, {1.000,0.000}, {3.000,2.000}}}, 4, 0.589885081, 0.999982974, {} }, // pathops_visualizer.htm:4152
|
|
{ {{{1.000,5.000}, {3.000,4.000}, {1.000,0.000}, {3.000,2.000}}}, 4, 0.589885081, 0.576935809, {} }, // pathops_visualizer.htm:4152
|
|
}; //
|
|
|
|
static IntersectData intersectDataSet9[] = { // pathops_visualizer.htm:4142
|
|
{ {{{0.000,1.000}, {2.000,3.000}, {5.000,1.000}, {4.000,3.000}}}, 4, 0.476627072, 0.311007457, {} }, // pathops_visualizer.htm:4100
|
|
{ {{{1.000,5.000}, {3.000,4.000}, {1.000,0.000}, {3.000,2.000}}}, 4, 0.999982974, 1, {} }, // pathops_visualizer.htm:4100
|
|
{ {{{0.000,1.000}, {2.000,3.000}, {5.000,1.000}, {4.000,3.000}}}, 4, 0.476627072, 1, {} }, // pathops_visualizer.htm:4100
|
|
}; //
|
|
|
|
static IntersectData intersectDataSet10[] = { // pathops_visualizer.htm:4186
|
|
{ {{{0.000,1.000}, {1.000,6.000}, {1.000,0.000}, {1.000,0.000}}}, 4, 0.788195121, 0.726275769, {} }, // pathops_visualizer.htm:4144
|
|
{ {{{0.000,1.000}, {0.000,1.000}, {1.000,0.000}, {6.000,1.000}}}, 4, 0.473378977, 1, {} }, // pathops_visualizer.htm:4144
|
|
{ {{{0.000,1.000}, {1.000,6.000}, {1.000,0.000}, {1.000,0.000}}}, 4, 0.788195121, 1, {} }, // pathops_visualizer.htm:4144
|
|
}; //
|
|
|
|
static IntersectData intersectDataSet11[] = { // pathops_visualizer.htm:4704
|
|
{ {{{979.305,561.000}, {1036.695,291.000}}}, 2, 0.888888874, 0.11111108, {} }, // pathops_visualizer.htm:4662
|
|
{ {{{1006.695,291.000}, {1023.264,291.000}, {1033.840,304.431}, {1030.318,321.000}}}, 4, 1, 0, {} }, // pathops_visualizer.htm:4662
|
|
{ {{{979.305,561.000}, {1036.695,291.000}}}, 2, 0.888888874, 1, {} }, // pathops_visualizer.htm:4662
|
|
}; //
|
|
|
|
static IntersectData intersectDataSet12[] = { // pathops_visualizer.htm:5481
|
|
{ {{{67.000,912.000}, {67.000,913.000}}}, 2, 1, 0, {} }, // pathops_visualizer.htm:5439
|
|
{ {{{67.000,913.000}, {67.000,917.389}, {67.224,921.726}, {67.662,926.000}}}, 4, 0, 1, {} }, // pathops_visualizer.htm:5439
|
|
{ {{{194.000,1041.000}, {123.860,1041.000}, {67.000,983.692}, {67.000,913.000}}}, 4, 1, 0, {} }, // pathops_visualizer.htm:5439
|
|
}; //
|
|
|
|
static IntersectData intersectDataSet13[] = { // pathops_visualizer.htm:5735
|
|
{ {{{6.000,0.000}, {0.000,4.000}}}, 2, 0.625, 0.25, {} }, // pathops_visualizer.htm:5693
|
|
{ {{{0.000,1.000}, {0.000,6.000}, {4.000,0.000}, {6.000,1.000}}}, 4, 0.5, 0.833333333, {} }, // pathops_visualizer.htm:5693
|
|
{ {{{0.000,1.000}, {0.000,6.000}, {4.000,0.000}, {6.000,1.000}}}, 4, 0.5, 0.379043969, {} }, // pathops_visualizer.htm:5693
|
|
}; //
|
|
|
|
static IntersectData intersectDataSet14[] = { // pathops_visualizer.htm:5875
|
|
{ {{{0.000,1.000}, {4.000,6.000}, {2.000,1.000}, {2.000,0.000}}}, 4, 0.0756502183, 0.0594570973, {} }, // pathops_visualizer.htm:5833
|
|
{ {{{1.000,2.000}, {0.000,2.000}, {1.000,0.000}, {6.000,4.000}}}, 4, 0.0756502184, 0, {} }, // pathops_visualizer.htm:5833
|
|
{ {{{0.000,1.000}, {4.000,6.000}, {2.000,1.000}, {2.000,0.000}}}, 4, 0.0756502183, 0.531917258, {} }, // pathops_visualizer.htm:5833
|
|
}; //
|
|
|
|
static IntersectData intersectDataSet15[] = { // pathops_visualizer.htm:6580
|
|
{ {{{490.435,879.407}, {405.593,909.436}}}, 2, 0.500554405, 1, {} }, // pathops_visualizer.htm:6538
|
|
{ {{{447.967,894.438}, {448.007,894.424}, {448.014,894.422}}}, 3, 0, 1, {} }, // pathops_visualizer.htm:6538
|
|
{ {{{490.435,879.407}, {405.593,909.436}}}, 2, 0.500554405, 0.500000273, {} }, // pathops_visualizer.htm:6538
|
|
}; //
|
|
|
|
static IntersectData intersectDataSet16[] = { // pathops_visualizer.htm:7419
|
|
{ {{{1.000,4.000}, {4.000,5.000}, {3.000,2.000}, {6.000,3.000}}}, 4, 0.5, 0, {} }, // pathops_visualizer.htm:7377
|
|
{ {{{2.000,3.000}, {3.000,6.000}, {4.000,1.000}, {5.000,4.000}}}, 4, 0.5, 0.112701665, {} }, // pathops_visualizer.htm:7377
|
|
{ {{{5.000,4.000}, {2.000,3.000}}}, 2, 0.5, 0, {} }, // pathops_visualizer.htm:7377
|
|
}; //
|
|
|
|
// from skpi_gino_com_16
|
|
static IntersectData intersectDataSet17[] = {
|
|
{ /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
|
|
, 3, 0.74590454, 0.547660352, {} },
|
|
{ /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
|
|
, 4, 0.12052623, 0, {} },
|
|
{ /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
|
|
, 3, 0.74590454, 1, {} },
|
|
};
|
|
|
|
static IntersectData intersectDataSet18[] = {
|
|
{ /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
|
|
, 3, 0.74590454, 1, {} },
|
|
{ /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
|
|
, 4, 0.12052623, 0.217351928, {} },
|
|
{ /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
|
|
, 3, 0.74590454, 0.547660352, {} },
|
|
};
|
|
|
|
static IntersectData intersectDataSet19[] = {
|
|
{ /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
|
|
, 4, 0.135148995, 0.134791946, {} },
|
|
{ /*seg=3*/ {{{1, 2}, {1, 2.15061641f}, {1, 2.21049166f}, {1.01366711f, 2.21379328f}}}
|
|
, 4, 0.956740456, 0.894913214, {} },
|
|
{ /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
|
|
, 4, 0.135148995, 0.551812363, {} },
|
|
};
|
|
|
|
#define I(x) intersectDataSet##x
|
|
|
|
static IntersectData* intersectDataSets[] = {
|
|
I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
|
|
I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
|
|
};
|
|
|
|
#undef I
|
|
#define I(x) (int) SK_ARRAY_COUNT(intersectDataSet##x)
|
|
|
|
static const int intersectDataSetSizes[] = {
|
|
I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
|
|
I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
|
|
};
|
|
|
|
#undef I
|
|
|
|
static const int intersectDataSetsSize = (int) SK_ARRAY_COUNT(intersectDataSetSizes);
|
|
|
|
struct FourPoints {
|
|
SkPoint pts[4];
|
|
};
|
|
|
|
DEF_TEST(PathOpsAngleAfter, reporter) {
|
|
SkChunkAlloc allocator(4096);
|
|
SkOpContourHead contour;
|
|
SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
|
contour.init(&state, false, false);
|
|
for (int index = intersectDataSetsSize - 1; index >= 0; --index) {
|
|
IntersectData* dataArray = intersectDataSets[index];
|
|
const int dataSize = intersectDataSetSizes[index];
|
|
for (int index2 = 0; index2 < dataSize - 2; ++index2) {
|
|
allocator.reset();
|
|
contour.reset();
|
|
for (int index3 = 0; index3 < 3; ++index3) {
|
|
IntersectData& data = dataArray[index2 + index3];
|
|
SkPoint* temp = (SkPoint*) SkOpTAllocator<FourPoints>::Allocate(&allocator);
|
|
for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
|
|
temp[idx2] = data.fPts.fPts[idx2].asSkPoint();
|
|
}
|
|
switch (data.fPtCount) {
|
|
case 2: {
|
|
contour.addLine(temp);
|
|
} break;
|
|
case 3: {
|
|
contour.addQuad(temp);
|
|
} break;
|
|
case 4: {
|
|
contour.addCubic(temp);
|
|
} break;
|
|
}
|
|
}
|
|
SkOpSegment* seg1 = contour.first();
|
|
seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd);
|
|
SkOpSegment* seg2 = seg1->next();
|
|
seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd);
|
|
SkOpSegment* seg3 = seg2->next();
|
|
seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd);
|
|
SkOpAngle& angle1 = *seg1->debugLastAngle();
|
|
SkOpAngle& angle2 = *seg2->debugLastAngle();
|
|
SkOpAngle& angle3 = *seg3->debugLastAngle();
|
|
PathOpsAngleTester::SetNext(angle1, angle3);
|
|
// These data sets are seeded when the set itself fails, so likely the dataset does not
|
|
// match the expected result. The tests above return 1 when first added, but
|
|
// return 0 after the bug is fixed.
|
|
SkDEBUGCODE(int result =) PathOpsAngleTester::After(angle2, angle1);
|
|
SkASSERT(result == 0 || result == 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SkOpSegment::debugAddAngle(double startT, double endT) {
|
|
SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT()
|
|
: this->addT(startT);
|
|
SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT()
|
|
: this->addT(endT);
|
|
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
|
|
SkOpSpanBase* startSpan = &fHead;
|
|
while (startSpan->ptT() != startPtT) {
|
|
startSpan = startSpan->upCast()->next();
|
|
}
|
|
SkOpSpanBase* endSpan = &fHead;
|
|
while (endSpan->ptT() != endPtT) {
|
|
endSpan = endSpan->upCast()->next();
|
|
}
|
|
angle->set(startSpan, endSpan);
|
|
if (startT < endT) {
|
|
startSpan->upCast()->setToAngle(angle);
|
|
endSpan->setFromAngle(angle);
|
|
} else {
|
|
endSpan->upCast()->setToAngle(angle);
|
|
startSpan->setFromAngle(angle);
|
|
}
|
|
}
|
|
|
|
DEF_TEST(PathOpsAngleAllOnOneSide, reporter) {
|
|
SkChunkAlloc allocator(4096);
|
|
SkOpContourHead contour;
|
|
SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
|
contour.init(&state, false, false);
|
|
SkPoint conicPts[3] = {{494.37100219726562f, 224.66200256347656f},
|
|
{494.37360910682298f, 224.6729026561527f},
|
|
{494.37600708007813f, 224.68400573730469f}};
|
|
SkPoint linePts[2] = {{494.371002f, 224.662003f}, {494.375000f, 224.675995f}};
|
|
for (int i = 10; i >= 0; --i) {
|
|
SkPoint modLinePts[2] = { linePts[0], linePts[1] };
|
|
modLinePts[1].fX += i * .1f;
|
|
contour.addLine(modLinePts);
|
|
contour.addQuad(conicPts);
|
|
// contour.addConic(conicPts, 0.999935746f, &allocator);
|
|
SkOpSegment* first = contour.first();
|
|
first->debugAddAngle(0, 1);
|
|
SkOpSegment* next = first->next();
|
|
next->debugAddAngle(0, 1);
|
|
/* int result = */
|
|
PathOpsAngleTester::AllOnOneSide(*first->debugLastAngle(), *next->debugLastAngle());
|
|
// SkDebugf("i=%d result=%d\n", i , result);
|
|
// SkDebugf("");
|
|
}
|
|
}
|