blob: 37973f8f33910285c9c90615590b4175c2d34027 [file] [log] [blame]
caryclark@google.comad65a3e2013-04-15 19:13:59 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.com8d0a5242013-07-16 16:11:16 +00007#include "PathOpsTestCommon.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00008#include "SkIntersections.h"
caryclark@google.comad65a3e2013-04-15 19:13:59 +00009#include "SkOpSegment.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000010#include "SkPathOpsTriangle.h"
11#include "SkRandom.h"
caryclark@google.comcffbcc32013-06-04 17:59:42 +000012#include "SkTArray.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000013#include "SkTSort.h"
caryclark@google.comad65a3e2013-04-15 19:13:59 +000014#include "Test.h"
15
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000016static bool gDisableAngleTests = true;
17
caryclark@google.comad65a3e2013-04-15 19:13:59 +000018static const SkPoint cubics[][4] = {
reed@google.com277c3f82013-05-31 15:17:50 +000019/* 0 */ {{0, 1}, {2, 6}, {4, 2}, {5, 3}},
20/* 1 */ {{10, 234}, {10, 229.581726f}, {13.5817204f, 226}, {18, 226}},
21/* 2 */ {{132, 11419}, {130.89543151855469f, 11419}, {130, 11418.1044921875f}, {130, 11417}},
22/* 3 */ {{130.04275512695312f, 11417.4130859375f}, {130.23307800292969f, 11418.3193359375f},
23 {131.03709411621094f, 11419}, {132, 11419}},
24/* 4 */ {{0,1}, {0,5}, {4,1}, {6,4}},
25/* 5 */ {{1,5}, {4,6}, {1,0}, {4,0}},
26/* 6 */ {{0,1}, {0,4}, {5,1}, {6,4}},
27/* 7 */ {{0,1}, {1,2}, {1,0}, {6,1}},
28/* 8 */ {{0,3}, {0,1}, {2,0}, {1,0}},
29/* 9 */ {{189,7}, {189,5.3431458473205566f}, {190.3431396484375f,4}, {192,4}},
30/* 10 */ {{0,1}, {1,3}, {1,0}, {6,4}},
31/* 11 */ {{0,1}, {2,3}, {2,1}, {4,3}},
32/* 12 */ {{1,2}, {3,4}, {1,0}, {3,2}},
caryclark@google.comcffbcc32013-06-04 17:59:42 +000033/* 13 */ {{0,1}, {4,6}, {4,3}, {5,4}},
caryclark@google.com4b66f362013-06-04 18:14:11 +000034/* 14 */ {{806,11419}, {806.962890625f,11419}, {807.76690673828125f,11418.3193359375f}, {807.957275390625f,11417.4130859375f}},
35/* 15 */ {{808,11417}, {808,11418.1044921875f}, {807.10455322265625f,11419}, {806,11419}},
36/* 16 */ {{132,11419}, {130.89543151855469f,11419}, {130,11418.1044921875f}, {130,11417}},
37/* 17 */ {{130.04275512695312f,11417.4130859375f}, {130.23312377929687f,11418.3193359375f}, {131.03707885742187f,11419}, {132,11419}},
caryclark@google.com07e97fc2013-07-08 17:17:02 +000038/* 18 */ {{1006.6951293945312f,291}, {1023.263671875f,291}, {1033.8402099609375f,304.43145751953125f}, {1030.318359375f,321}},
caryclark@google.coma5e55922013-05-07 18:51:31 +000039};
40
41static const SkPoint quads[][3] = {
reed@google.com277c3f82013-05-31 15:17:50 +000042/* 0 */ {{12.3423996f, 228.342407f}, {10, 230.686295f}, {10, 234}},
43/* 1 */ {{304.24319458007812f,591.75677490234375f}, {306,593.51470947265625f}, {306,596}},
caryclark@google.comcffbcc32013-06-04 17:59:42 +000044/* 2 */ {{0,0}, {3,1}, {0,3}},
45/* 3 */ {{0,1}, {3,1}, {0,2}},
caryclark@google.comad65a3e2013-04-15 19:13:59 +000046};
47
48static const SkPoint lines[][2] = {
reed@google.com277c3f82013-05-31 15:17:50 +000049/* 0 */ {{6, 2}, {2, 4}},
50/* 1 */ {{306,617}, {306,590}},
51/* 2 */ {{306,596}, {306,617}},
52/* 3 */ {{6,4}, {0,1}},
53/* 4 */ {{6,1}, {0,1}},
54/* 5 */ {{1,0}, {0,3}},
55/* 6 */ {{246,4}, {189,4}},
56/* 7 */ {{192,4}, {243,4}},
57/* 8 */ {{4,3}, {0,1}},
58/* 9 */ {{3,2}, {1,2}},
caryclark@google.comcffbcc32013-06-04 17:59:42 +000059/* 10 */ {{6,4}, {3,4}},
caryclark@google.com07e97fc2013-07-08 17:17:02 +000060/* 11 */ {{979.30487060546875f,561}, {1036.695068359375f,291}},
caryclark@google.comad65a3e2013-04-15 19:13:59 +000061};
62
63struct SortSet {
64 const SkPoint* ptData;
65 int ptCount;
66 double tStart;
67 double tEnd;
caryclark@google.comcffbcc32013-06-04 17:59:42 +000068 SkPoint endPt;
caryclark@google.comad65a3e2013-04-15 19:13:59 +000069};
70
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000071/*static const SortSet set1[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +000072 {cubics[0], 4, 0.66666987081928919, 0.875, {0, 0}},
73 {lines[0], 2, 0.574070336, 0.388888889, {0, 0}},
74 {cubics[0], 4, 0.66666987081928919, 0.4050371120499307, {0, 0}},
75 {lines[0], 2, 0.574070336, 0.9140625, {0, 0}},
76};
77
78static const SortSet set1a[] = {
79 {cubics[0], 4, 0.666666667, 0.405037112, {4.58007812f,2.83203125f}},
80 {lines[0], 2, 0.574074074, 0.9140625, {4.44444466f,2.77777767f}},
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000081};*/
caryclark@google.comad65a3e2013-04-15 19:13:59 +000082
83static const SortSet set2[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +000084 {cubics[0], 4, 0.666666667, 0.875, {0, 0}},
85 {lines[0], 2, 0.574074074, 0.388888889, {0, 0}},
86 {cubics[0], 4, 0.666666667, 0.405037112, {0, 0}},
87 {lines[0], 2, 0.574074074, 0.9140625, {0, 0}},
caryclark@google.comad65a3e2013-04-15 19:13:59 +000088};
89
caryclark@google.coma5e55922013-05-07 18:51:31 +000090static const SortSet set3[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +000091 {cubics[1], 4, 0, 1, {0, 0}},
92 {quads[0], 3, 1, 0, {0, 0}},
caryclark@google.coma5e55922013-05-07 18:51:31 +000093};
94
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000095/*static const SortSet set4[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +000096 {cubics[2], 4, 0.812114222, 1, {0, 0}},
97 {cubics[3], 4, 0.0684734759, 0, {0, 0}},
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000098};*/
reed@google.com277c3f82013-05-31 15:17:50 +000099
100static const SortSet set5[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000101 {lines[1], 2, 0.777777778, 1, {0, 0}},
102 {quads[1], 3, 1, 4.34137342e-06, {0, 0}},
103 {lines[2], 2, 0, 1, {0, 0}},
104};
105
106static const SortSet set5a[] = {
107 {lines[1], 2, 0.777777778, 1, {306,590}},
108 {quads[1], 3, 1, 4.34137342e-06, {304.243195f,591.756775f}},
109 {lines[2], 2, 0, 1, {306,617}},
reed@google.com277c3f82013-05-31 15:17:50 +0000110};
111
112static const SortSet set6[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000113 {lines[3], 2, 0.407407407, 0.554627832, {0, 0}},
114 {cubics[4], 4, 0.666666667, 0.548022446, {0, 0}},
115 {lines[3], 2, 0.407407407, 0, {0, 0}},
116 {cubics[4], 4, 0.666666667, 1, {0, 0}},
117};
118
119static const SortSet set6a[] = {
120 {lines[3], 2, 0.407407407, 0.554627832, {2.6722331f,2.33611655f}},
121 {cubics[4], 4, 0.666666667, 0.548022446, {2.61642241f,2.83718514f}},
122 {lines[3], 2, 0.407407407, 0, {6,4}},
123 {cubics[4], 4, 0.666666667, 1, {6,4}},
reed@google.com277c3f82013-05-31 15:17:50 +0000124};
125
126static const SortSet set7[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000127 {cubics[5], 4, 0.545233342, 0.545454545, {0, 0}},
128 {cubics[6], 4, 0.484938134, 0.484805744, {0, 0}},
129 {cubics[5], 4, 0.545233342, 0, {0, 0}},
130 {cubics[6], 4, 0.484938134, 0.545454545, {0, 0}},
reed@google.com277c3f82013-05-31 15:17:50 +0000131};
132
133static const SortSet set8[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000134 {cubics[7], 4, 0.5, 0.522986744, {0, 0}},
135 {lines[4], 2, 0.75, 1, {0, 0}},
136 {cubics[7], 4, 0.5, 0, {0, 0}},
137 {lines[4], 2, 0.75, 0.737654321, {0, 0}},
138};
139
140static const SortSet set8a[] = {
141 {cubics[7], 4, 0.5, 0.522986744, {1.60668361f,0.965592742f}},
142 {lines[4], 2, 0.75, 1, {0,1}},
143 {cubics[7], 4, 0.5, 0, {0,1}},
144 {lines[4], 2, 0.75, 0.737654321, {1.57407403f,1}},
reed@google.com277c3f82013-05-31 15:17:50 +0000145};
146
147static const SortSet set9[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000148 {cubics[8], 4, 0.4, 1, {0, 0}},
149 {lines[5], 2, 0.36, 0, {0, 0}},
150 {cubics[8], 4, 0.4, 0.394675838, {0, 0}},
151 {lines[5], 2, 0.36, 0.363999782, {0, 0}},
reed@google.com277c3f82013-05-31 15:17:50 +0000152};
153
154static const SortSet set10[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000155 {lines[6], 2, 0.947368421, 1, {0, 0}},
156 {cubics[9], 4, 1, 0.500000357, {0, 0}},
157 {lines[7], 2, 0, 1, {0, 0}},
reed@google.com277c3f82013-05-31 15:17:50 +0000158};
159
160static const SortSet set11[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000161 {lines[3], 2, 0.75, 1, {0, 0}},
162 {cubics[10], 4, 0.5, 0.228744269, {0, 0}},
163 {lines[3], 2, 0.75, 0.627112191, {0, 0}},
164 {cubics[10], 4, 0.5, 0.6339746, {0, 0}},
reed@google.com277c3f82013-05-31 15:17:50 +0000165};
166
167static const SortSet set12[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000168 {cubics[12], 4, 0.5, 1, {0, 0}},
169 {lines[8], 2, 0.5, 1, {0, 0}},
170 {cubics[11], 4, 0.5, 0, {0, 0}},
171 {lines[9], 2, 0.5, 1, {0, 0}},
172 {cubics[12], 4, 0.5, 0, {0, 0}},
173 {lines[8], 2, 0.5, 0, {0, 0}},
174 {cubics[11], 4, 0.5, 1, {0, 0}},
175 {lines[9], 2, 0.5, 0, {0, 0}},
176};
177
mtklein@google.com6e223af2013-11-22 20:43:54 +0000178/*static const SortSet set13[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000179 {cubics[13], 4, 0.5, 0.400631046, {0, 0}},
180 {lines[10], 2, 0.791666667, 0.928, {0, 0}},
181 {lines[10], 2, 0.791666667, 0.333333333, {0, 0}},
182 {cubics[13], 4, 0.5, 0.866666667, {0, 0}},
mtklein@google.com6e223af2013-11-22 20:43:54 +0000183};*/
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000184
185static const SortSet set14[] = {
186 {quads[2], 3, 0.5, 0.310102051, {0, 0}},
187 {quads[3], 3, 0.5, 0.2, {0, 0}},
188 {quads[3], 3, 0.5, 0.770156212, {0, 0}},
189 {quads[2], 3, 0.5, 0.7, {0, 0}},
190};
191
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +0000192/*static const SortSet set15[] = {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000193 {cubics[14], 4, 0.93081374, 1, {0, 0}},
194 {cubics[15], 4, 0.188518131, 0, {0, 0}},
195 {cubics[14], 4, 0.93081374, 0, {0, 0}},
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +0000196};*/
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000197
198static const SortSet set16[] = {
199 {cubics[17], 4, 0.0682619216, 0, {130.042755f,11417.4131f}},
200 {cubics[16], 4, 0.812302088, 1, {130,11417}},
201 {cubics[17], 4, 0.0682619216, 1, {132,11419}},
reed@google.com277c3f82013-05-31 15:17:50 +0000202};
203
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000204static const SortSet set17[] = {
205 {lines[11], 2, 0.888889581, 1, {0, 0}},
206 {cubics[18], 4, 0.999996241, 0, {0, 0}},
207 {lines[11], 2, 0.888889581, 0, {0, 0}},
208 {cubics[18], 4, 0.999996241, 1, {0, 0}},
209};
210
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000211struct SortSetTests {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000212 const char* name;
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000213 const SortSet* set;
214 size_t count;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000215 SkPoint startPt;
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000216};
217
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000218#define TEST_ENTRY(name) #name, name, SK_ARRAY_COUNT(name)
219
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000220static const SortSetTests tests[] = {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000221 { TEST_ENTRY(set17), {0, 0}},
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000222 { TEST_ENTRY(set16), {130.090179f,11417.5957f} },
223// { TEST_ENTRY(set15), {0, 0}},
224 { TEST_ENTRY(set14), {0, 0}},
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000225// { TEST_ENTRY(set13), {0, 0}},
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000226 { TEST_ENTRY(set12), {0, 0}},
227 { TEST_ENTRY(set11), {0, 0}},
228 { TEST_ENTRY(set10), {0, 0}},
229 { TEST_ENTRY(set9), {0, 0}},
230 { TEST_ENTRY(set6a), {3.55555558f,2.77777767f} },
231 { TEST_ENTRY(set8a), {1.5f,1} },
232 { TEST_ENTRY(set8), {0, 0}},
233 { TEST_ENTRY(set7), {0, 0}},
234 { TEST_ENTRY(set6a), {3.55555558f,2.77777767f} },
235 { TEST_ENTRY(set6), {0, 0}},
236 { TEST_ENTRY(set5a), {306,596} },
237 { TEST_ENTRY(set5), {0, 0}},
238// { TEST_ENTRY(set4), {0, 0}},
239 { TEST_ENTRY(set3), {0, 0}},
240 { TEST_ENTRY(set2), {0, 0}},
241// { TEST_ENTRY(set1a), {3.70370364f,3.14814806f} },
caryclark@google.com570863f2013-09-16 15:55:01 +0000242// { TEST_ENTRY(set1), {0, 0}},
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000243};
244
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000245#undef TEST_ENTRY
246
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000247static float next(float f)
248{
249 int fBits = SkFloatAs2sCompliment(f);
250 ++fBits;
251 float fNext = Sk2sComplimentAsFloat(fBits);
252 return fNext;
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000253}
254
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000255static float prev(float f)
256{
257 int fBits = SkFloatAs2sCompliment(f);
258 --fBits;
259 float fNext = Sk2sComplimentAsFloat(fBits);
260 return fNext;
261}
262
263DEF_TEST(PathOpsAngleFindCrossEpsilon, reporter) {
264 if (gDisableAngleTests) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000265 return;
266 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000267 SkRandom ran;
268 int maxEpsilon = 0;
269 for (int index = 0; index < 10000000; ++index) {
270 SkDLine line = {{{0, 0}, {ran.nextRangeF(0.0001f, 1000), ran.nextRangeF(0.0001f, 1000)}}};
271 for (int inner = 0; inner < 10; ++inner) {
272 float t = ran.nextRangeF(0.0001f, 1);
273 SkDPoint dPt = line.ptAtT(t);
274 SkPoint pt = dPt.asSkPoint();
275 float xs[3] = { prev(pt.fX), pt.fX, next(pt.fX) };
276 float ys[3] = { prev(pt.fY), pt.fY, next(pt.fY) };
277 for (int xIdx = 0; xIdx < 3; ++xIdx) {
278 for (int yIdx = 0; yIdx < 3; ++yIdx) {
279 SkPoint test = { xs[xIdx], ys[yIdx] };
280 float p1 = SkDoubleToScalar(line[1].fX * test.fY);
281 float p2 = SkDoubleToScalar(line[1].fY * test.fX);
282 int p1Bits = SkFloatAs2sCompliment(p1);
283 int p2Bits = SkFloatAs2sCompliment(p2);
284 int epsilon = abs(p1Bits - p2Bits);
285 if (maxEpsilon < epsilon) {
286 SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g pt={%1.7g, %1.7g}"
287 " epsilon=%d\n",
288 line[1].fX, line[1].fY, t, test.fX, test.fY, epsilon);
289 maxEpsilon = epsilon;
290 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000291 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000292 }
293 }
294 }
295}
296
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000297DEF_TEST(PathOpsAngleFindQuadEpsilon, reporter) {
298 if (gDisableAngleTests) {
299 return;
300 }
301 SkRandom ran;
302 int maxEpsilon = 0;
303 double maxAngle = 0;
304 for (int index = 0; index < 100000; ++index) {
305 SkDLine line = {{{0, 0}, {ran.nextRangeF(0.0001f, 1000), ran.nextRangeF(0.0001f, 1000)}}};
306 float t = ran.nextRangeF(0.0001f, 1);
307 SkDPoint dPt = line.ptAtT(t);
308 float t2 = ran.nextRangeF(0.0001f, 1);
309 SkDPoint qPt = line.ptAtT(t2);
310 float t3 = ran.nextRangeF(0.0001f, 1);
311 SkDPoint qPt2 = line.ptAtT(t3);
312 qPt.fX += qPt2.fY;
313 qPt.fY -= qPt2.fX;
314 SkDQuad quad = {{line[0], dPt, qPt}};
315 // binary search for maximum movement of quad[1] towards test that still has 1 intersection
316 double moveT = 0.5f;
317 double deltaT = moveT / 2;
318 SkDPoint last;
319 do {
320 last = quad[1];
321 quad[1].fX = dPt.fX - line[1].fY * moveT;
322 quad[1].fY = dPt.fY + line[1].fX * moveT;
323 SkIntersections i;
324 i.intersect(quad, line);
325 REPORTER_ASSERT(reporter, i.used() > 0);
326 if (i.used() == 1) {
327 moveT += deltaT;
328 } else {
329 moveT -= deltaT;
330 }
331 deltaT /= 2;
332 } while (last.asSkPoint() != quad[1].asSkPoint());
333 float p1 = SkDoubleToScalar(line[1].fX * last.fY);
334 float p2 = SkDoubleToScalar(line[1].fY * last.fX);
335 int p1Bits = SkFloatAs2sCompliment(p1);
336 int p2Bits = SkFloatAs2sCompliment(p2);
337 int epsilon = abs(p1Bits - p2Bits);
338 if (maxEpsilon < epsilon) {
339 SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g"
340 " pt={%1.7g, %1.7g} epsilon=%d\n",
341 line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, epsilon);
342 maxEpsilon = epsilon;
343 }
344 double a1 = atan2(line[1].fY, line[1].fX);
345 double a2 = atan2(last.fY, last.fX);
346 double angle = fabs(a1 - a2);
347 if (maxAngle < angle) {
348 SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g"
349 " pt={%1.7g, %1.7g} angle=%1.7g\n",
350 line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, angle);
351 maxAngle = angle;
352 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000353 }
354}
355
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000356static int find_slop(double x, double y, double rx, double ry) {
357 int slopBits = 0;
358 bool less1, less2;
359 double absX = fabs(x);
360 double absY = fabs(y);
361 double length = absX < absY ? absX / 2 + absY : absX + absY / 2;
362 int exponent;
363 (void) frexp(length, &exponent);
364 double epsilon = ldexp(FLT_EPSILON, exponent);
365 do {
366 // get the length as the larger plus half the smaller (both same signs)
367 // find the ulps of the length
368 // compute the offsets from there
369 double xSlop = epsilon * slopBits;
370 double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _copysign ?
371 double x1 = x - xSlop;
372 double y1 = y + ySlop;
373 double x_ry1 = x1 * ry;
374 double rx_y1 = rx * y1;
375 less1 = x_ry1 < rx_y1;
376 double x2 = x + xSlop;
377 double y2 = y - ySlop;
378 double x_ry2 = x2 * ry;
379 double rx_y2 = rx * y2;
380 less2 = x_ry2 < rx_y2;
381 } while (less1 == less2 && ++slopBits);
382 return slopBits;
383}
384
385// from http://stackoverflow.com/questions/1427422/cheap-algorithm-to-find-measure-of-angle-between-vectors
386static double diamond_angle(double y, double x)
387{
388 if (y >= 0)
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000389 return (x >= 0 ? y/(x+y) : 1-x/(-x+y));
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000390 else
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000391 return (x < 0 ? 2-y/(-x-y) : 3+x/(x-y));
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000392}
393
394static const double slopTests[][4] = {
395 // x y rx ry
396 {-0.058554756452593892, -0.18804585843827226, -0.018568569646021160, -0.059615294434479438},
397 {-0.0013717412948608398, 0.0041152238845825195, -0.00045837944195925573, 0.0013753175735478074},
398 {-2.1033774145221198, -1.4046019261273715e-008, -0.70062688352066704, -1.2706324683777995e-008},
399};
400
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000401DEF_TEST(PathOpsAngleFindSlop, reporter) {
402 if (gDisableAngleTests) {
403 return;
404 }
405 for (int index = 0; index < (int) SK_ARRAY_COUNT(slopTests); ++index) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000406 const double* slopTest = slopTests[index];
407 double x = slopTest[0];
408 double y = slopTest[1];
409 double rx = slopTest[2];
410 double ry = slopTest[3];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000411 SkDebugf("%s xy %d=%d\n", __FUNCTION__, index, find_slop(x, y, rx, ry));
412 SkDebugf("%s rxy %d=%d\n", __FUNCTION__, index, find_slop(rx, ry, x, y));
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000413 double angle = diamond_angle(y, x);
414 double rAngle = diamond_angle(ry, rx);
415 double diff = fabs(angle - rAngle);
416 SkDebugf("%s diamond xy=%1.9g rxy=%1.9g diff=%1.9g factor=%d\n", __FUNCTION__,
417 angle, rAngle, diff, (int) (diff / FLT_EPSILON));
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000418 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000419}
420
421class PathOpsAngleTester {
422public:
423 static int After(const SkOpAngle& lh, const SkOpAngle& rh) {
424 return lh.after(&rh);
425 }
426
427 static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) {
428 return lh.convexHullOverlaps(rh);
429 }
430
431 static int Orderable(const SkOpAngle& lh, const SkOpAngle& rh) {
432 return lh.orderable(rh);
433 }
434
435 static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) {
436 return lh.endsIntersect(rh);
437 }
438
439 static void SetNext(SkOpAngle& lh, SkOpAngle& rh) {
440 lh.fNext = &rh;
441 }
442};
443
444class PathOpsSegmentTester {
445public:
446 static void ConstructCubic(SkOpSegment* segment, SkPoint shortCubic[4]) {
447 segment->debugConstructCubic(shortCubic);
448 }
449
450 static void ConstructLine(SkOpSegment* segment, SkPoint shortLine[2]) {
451 segment->debugConstructLine(shortLine);
452 }
453
454 static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) {
455 segment->debugConstructQuad(shortQuad);
456 }
457
458 static void DebugReset(SkOpSegment* segment) {
459 segment->debugReset();
460 }
461};
462
463struct CircleData {
464 const SkDCubic fPts;
465 const int fPtCount;
466 SkPoint fShortPts[4];
467};
468
469static CircleData circleDataSet[] = {
470 { {{{313.0155029296875, 207.90290832519531}, {320.05078125, 227.58743286132812}}}, 2, {} },
471 { {{{313.0155029296875, 207.90290832519531}, {313.98246891063195, 219.33615203830394},
472 {320.05078125, 227.58743286132812}}}, 3, {} },
473};
474
475static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
476
477DEF_TEST(PathOpsAngleCircle, reporter) {
478 SkOpSegment segment[2];
479 for (int index = 0; index < circleDataSetSize; ++index) {
480 CircleData& data = circleDataSet[index];
481 for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
482 data.fShortPts[idx2] = data.fPts.fPts[idx2].asSkPoint();
483 }
484 switch (data.fPtCount) {
485 case 2:
486 PathOpsSegmentTester::ConstructLine(&segment[index], data.fShortPts);
487 break;
488 case 3:
489 PathOpsSegmentTester::ConstructQuad(&segment[index], data.fShortPts);
490 break;
491 case 4:
492 PathOpsSegmentTester::ConstructCubic(&segment[index], data.fShortPts);
493 break;
494 }
495 }
496 PathOpsAngleTester::Orderable(segment[0].angle(0), segment[1].angle(0));
497}
498
499struct IntersectData {
500 const SkDCubic fPts;
501 const int fPtCount;
502 double fTStart;
503 double fTEnd;
504 SkPoint fShortPts[4];
505};
506
507static IntersectData intersectDataSet1[] = {
508 { {{{322.935669,231.030273}, {312.832214,220.393295}, {312.832214,203.454178}}}, 3,
509 0.865309956, 0.154740299, {} },
510 { {{{322.12738,233.397751}, {295.718353,159.505829}}}, 2,
511 0.345028807, 0.0786326511, {} },
512 { {{{322.935669,231.030273}, {312.832214,220.393295}, {312.832214,203.454178}}}, 3,
513 0.865309956, 1, {} },
514 { {{{322.12738,233.397751}, {295.718353,159.505829}}}, 2,
515 0.345028807, 1, {} },
516};
517
518static IntersectData intersectDataSet2[] = {
519 { {{{364.390686,157.898193}, {375.281769,136.674606}, {396.039917,136.674606}}}, 3,
520 0.578520747, 1, {} },
521 { {{{364.390686,157.898193}, {375.281769,136.674606}, {396.039917,136.674606}}}, 3,
522 0.578520747, 0.536512973, {} },
523 { {{{366.608826,151.196014}, {378.803101,136.674606}, {398.164948,136.674606}}}, 3,
524 0.490456543, 1, {} },
525};
526
527static IntersectData intersectDataSet3[] = {
528 { {{{2.000000,0.000000}, {1.33333333,0.66666667}}}, 2, 1, 0, {} },
529 { {{{1.33333333,0.66666667}, {0.000000,2.000000}}}, 2, 0, 0.25, {} },
530 { {{{2.000000,2.000000}, {1.33333333,0.66666667}}}, 2, 1, 0, {} },
531};
532
533static IntersectData intersectDataSet4[] = {
534 { {{{1.3333333,0.6666667}, {0.000,2.000}}}, 2, 0.250000006, 0, {} },
535 { {{{1.000,0.000}, {1.000,1.000}}}, 2, 1, 0, {} },
536 { {{{1.000,1.000}, {0.000,0.000}}}, 2, 0, 1, {} },
537};
538
539static IntersectData intersectDataSet5[] = {
540 { {{{0.000,0.000}, {1.000,0.000}, {1.000,1.000}}}, 3, 1, 0.666666667, {} },
541 { {{{0.000,0.000}, {2.000,1.000}, {0.000,2.000}}}, 3, 0.5, 1, {} },
542 { {{{0.000,0.000}, {2.000,1.000}, {0.000,2.000}}}, 3, 0.5, 0, {} },
543};
544
545static IntersectData intersectDataSet6[] = { // pathops_visualizer.htm:3658
546 { {{{0.000,1.000}, {3.000,4.000}, {1.000,0.000}, {3.000,0.000}}}, 4, 0.0925339054, 0, {} }, // pathops_visualizer.htm:3616
547 { {{{0.000,1.000}, {0.000,3.000}, {1.000,0.000}, {4.000,3.000}}}, 4, 0.453872386, 0, {} }, // pathops_visualizer.htm:3616
548 { {{{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
549};
550
551static IntersectData intersectDataSet7[] = { // pathops_visualizer.htm:3748
552 { {{{2.000,1.000}, {0.000,1.000}}}, 2, 0.5, 0, {} }, // pathops_visualizer.htm:3706
553 { {{{2.000,0.000}, {0.000,2.000}}}, 2, 0.5, 1, {} }, // pathops_visualizer.htm:3706
554 { {{{0.000,1.000}, {0.000,2.000}, {2.000,0.000}, {2.000,1.000}}}, 4, 0.5, 1, {} }, // pathops_visualizer.htm:3706
555}; //
556
557static IntersectData intersectDataSet8[] = { // pathops_visualizer.htm:4194
558 { {{{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
559 { {{{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
560 { {{{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
561}; //
562
563static IntersectData intersectDataSet9[] = { // pathops_visualizer.htm:4142
564 { {{{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
565 { {{{1.000,5.000}, {3.000,4.000}, {1.000,0.000}, {3.000,2.000}}}, 4, 0.999982974, 1, {} }, // pathops_visualizer.htm:4100
566 { {{{0.000,1.000}, {2.000,3.000}, {5.000,1.000}, {4.000,3.000}}}, 4, 0.476627072, 1, {} }, // pathops_visualizer.htm:4100
567}; //
568
569static IntersectData intersectDataSet10[] = { // pathops_visualizer.htm:4186
570 { {{{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
571 { {{{0.000,1.000}, {0.000,1.000}, {1.000,0.000}, {6.000,1.000}}}, 4, 0.473378977, 1, {} }, // pathops_visualizer.htm:4144
572 { {{{0.000,1.000}, {1.000,6.000}, {1.000,0.000}, {1.000,0.000}}}, 4, 0.788195121, 1, {} }, // pathops_visualizer.htm:4144
573}; //
574
575static IntersectData intersectDataSet11[] = { // pathops_visualizer.htm:4704
576 { {{{979.305,561.000}, {1036.695,291.000}}}, 2, 0.888888874, 0.11111108, {} }, // pathops_visualizer.htm:4662
577 { {{{1006.695,291.000}, {1023.264,291.000}, {1033.840,304.431}, {1030.318,321.000}}}, 4, 1, 0, {} }, // pathops_visualizer.htm:4662
578 { {{{979.305,561.000}, {1036.695,291.000}}}, 2, 0.888888874, 1, {} }, // pathops_visualizer.htm:4662
579}; //
580
581static IntersectData intersectDataSet12[] = { // pathops_visualizer.htm:5481
582 { {{{67.000,912.000}, {67.000,913.000}}}, 2, 1, 0, {} }, // pathops_visualizer.htm:5439
583 { {{{67.000,913.000}, {67.000,917.389}, {67.224,921.726}, {67.662,926.000}}}, 4, 0, 1, {} }, // pathops_visualizer.htm:5439
584 { {{{194.000,1041.000}, {123.860,1041.000}, {67.000,983.692}, {67.000,913.000}}}, 4, 1, 0, {} }, // pathops_visualizer.htm:5439
585}; //
586
587static IntersectData intersectDataSet13[] = { // pathops_visualizer.htm:5735
588 { {{{6.000,0.000}, {0.000,4.000}}}, 2, 0.625, 0.25, {} }, // pathops_visualizer.htm:5693
589 { {{{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
590 { {{{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
591}; //
592
593static IntersectData intersectDataSet14[] = { // pathops_visualizer.htm:5875
594 { {{{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
595 { {{{1.000,2.000}, {0.000,2.000}, {1.000,0.000}, {6.000,4.000}}}, 4, 0.0756502184, 0, {} }, // pathops_visualizer.htm:5833
596 { {{{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
597}; //
598
599static IntersectData intersectDataSet15[] = { // pathops_visualizer.htm:6580
600 { {{{490.435,879.407}, {405.593,909.436}}}, 2, 0.500554405, 1, {} }, // pathops_visualizer.htm:6538
601 { {{{447.967,894.438}, {448.007,894.424}, {448.014,894.422}}}, 3, 0, 1, {} }, // pathops_visualizer.htm:6538
602 { {{{490.435,879.407}, {405.593,909.436}}}, 2, 0.500554405, 0.500000273, {} }, // pathops_visualizer.htm:6538
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +0000603}; //
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000604
605static IntersectData intersectDataSet16[] = { // pathops_visualizer.htm:7419
606 { {{{1.000,4.000}, {4.000,5.000}, {3.000,2.000}, {6.000,3.000}}}, 4, 0.5, 0, {} }, // pathops_visualizer.htm:7377
607 { {{{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
608 { {{{5.000,4.000}, {2.000,3.000}}}, 2, 0.5, 0, {} }, // pathops_visualizer.htm:7377
609}; //
610
611#define I(x) intersectDataSet##x
612
613static IntersectData* intersectDataSets[] = {
614 I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
615 I(11), I(12), I(13), I(14), I(15), I(16),
616};
617
618#undef I
619#define I(x) (int) SK_ARRAY_COUNT(intersectDataSet##x)
620
621static const int intersectDataSetSizes[] = {
622 I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
623 I(11), I(12), I(13), I(14), I(15), I(16),
624};
625
626#undef I
627
628static const int intersectDataSetsSize = (int) SK_ARRAY_COUNT(intersectDataSetSizes);
629
630DEF_TEST(PathOpsAngleAfter, reporter) {
631 for (int index = intersectDataSetsSize - 1; index >= 0; --index) {
632 IntersectData* dataArray = intersectDataSets[index];
633 const int dataSize = intersectDataSetSizes[index];
634 SkOpSegment segment[3];
635 for (int index2 = 0; index2 < dataSize - 2; ++index2) {
636 for (int temp = 0; temp < (int) SK_ARRAY_COUNT(segment); ++temp) {
637 PathOpsSegmentTester::DebugReset(&segment[temp]);
638 }
639 for (int index3 = 0; index3 < (int) SK_ARRAY_COUNT(segment); ++index3) {
640 IntersectData& data = dataArray[index2 + index3];
641 SkPoint temp[4];
642 for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
643 temp[idx2] = data.fPts.fPts[idx2].asSkPoint();
644 }
645 switch (data.fPtCount) {
646 case 2: {
647 SkDLine seg = SkDLine::SubDivide(temp, data.fTStart,
648 data.fTStart < data.fTEnd ? 1 : 0);
649 data.fShortPts[0] = seg[0].asSkPoint();
650 data.fShortPts[1] = seg[1].asSkPoint();
651 PathOpsSegmentTester::ConstructLine(&segment[index3], data.fShortPts);
652 } break;
653 case 3: {
654 SkDQuad seg = SkDQuad::SubDivide(temp, data.fTStart, data.fTEnd);
655 data.fShortPts[0] = seg[0].asSkPoint();
656 data.fShortPts[1] = seg[1].asSkPoint();
657 data.fShortPts[2] = seg[2].asSkPoint();
658 PathOpsSegmentTester::ConstructQuad(&segment[index3], data.fShortPts);
659 } break;
660 case 4: {
661 SkDCubic seg = SkDCubic::SubDivide(temp, data.fTStart, data.fTEnd);
662 data.fShortPts[0] = seg[0].asSkPoint();
663 data.fShortPts[1] = seg[1].asSkPoint();
664 data.fShortPts[2] = seg[2].asSkPoint();
665 data.fShortPts[3] = seg[3].asSkPoint();
666 PathOpsSegmentTester::ConstructCubic(&segment[index3], data.fShortPts);
667 } break;
668 }
669 }
670 SkOpAngle& angle1 = const_cast<SkOpAngle&>(segment[0].angle(0));
671 SkOpAngle& angle2 = const_cast<SkOpAngle&>(segment[1].angle(0));
672 SkOpAngle& angle3 = const_cast<SkOpAngle&>(segment[2].angle(0));
673 PathOpsAngleTester::SetNext(angle1, angle3);
674 // These data sets are seeded when the set itself fails, so likely the dataset does not
675 // match the expected result. The tests above return 1 when first added, but
676 // return 0 after the bug is fixed.
677 SkDEBUGCODE(int result =) PathOpsAngleTester::After(angle2, angle1);
678 SkASSERT(result == 0 || result == 1);
679 }
680 }
681}
682
683void SkOpSegment::debugConstruct() {
684 addStartSpan(1);
685 addEndSpan(1);
686 debugAddAngle(0, 1);
687}
688
689void SkOpSegment::debugAddAngle(int start, int end) {
690 SkASSERT(start != end);
691 SkOpAngle& angle = fAngles.push_back();
692 angle.set(this, start, end);
693}
694
695void SkOpSegment::debugConstructCubic(SkPoint shortQuad[4]) {
696 addCubic(shortQuad, false, false);
697 addT(NULL, shortQuad[0], 0);
698 addT(NULL, shortQuad[3], 1);
699 debugConstruct();
700}
701
702void SkOpSegment::debugConstructLine(SkPoint shortQuad[2]) {
703 addLine(shortQuad, false, false);
704 addT(NULL, shortQuad[0], 0);
705 addT(NULL, shortQuad[1], 1);
706 debugConstruct();
707}
708
709void SkOpSegment::debugConstructQuad(SkPoint shortQuad[3]) {
710 addQuad(shortQuad, false, false);
711 addT(NULL, shortQuad[0], 0);
712 addT(NULL, shortQuad[2], 1);
713 debugConstruct();
714}