blob: 8dc862760924b2d05ede5a0a6ce00bbd1c91c4cf [file] [log] [blame]
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001#include "EdgeWalker_Test.h"
2#include "Intersection_Tests.h"
3
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00004static void testSimplifyCoincidentInner() {
5 SkPath path, out;
6 path.setFillType(SkPath::kWinding_FillType);
7 path.addRect(10, 10, 60, 60, SkPath::kCCW_Direction);
8 path.addRect(20, 20, 50, 50, SkPath::kCW_Direction);
9 path.addRect(20, 30, 40, 40, SkPath::kCW_Direction);
10 testSimplify(path, true, out);
11}
12
caryclark@google.comcd4421d2012-03-01 19:16:31 +000013static void testSimplifyCoincidentVertical() {
14 SkPath path, out;
15 path.setFillType(SkPath::kWinding_FillType);
16 path.addRect(10, 10, 30, 30);
17 path.addRect(10, 30, 30, 40);
18 simplify(path, true, out);
19 SkRect rect;
20 if (!out.isRect(&rect)) {
21 SkDebugf("%s expected rect\n", __FUNCTION__);
22 }
23 if (rect != SkRect::MakeLTRB(10, 10, 30, 40)) {
24 SkDebugf("%s expected union\n", __FUNCTION__);
25 }
26}
27
28static void testSimplifyCoincidentHorizontal() {
29 SkPath path, out;
30 path.setFillType(SkPath::kWinding_FillType);
31 path.addRect(10, 10, 30, 30);
32 path.addRect(30, 10, 40, 30);
33 simplify(path, true, out);
34 SkRect rect;
35 if (!out.isRect(&rect)) {
36 SkDebugf("%s expected rect\n", __FUNCTION__);
37 }
38 if (rect != SkRect::MakeLTRB(10, 10, 40, 30)) {
39 SkDebugf("%s expected union\n", __FUNCTION__);
40 }
41}
42
43static void testSimplifyMulti() {
44 SkPath path, out;
45 path.setFillType(SkPath::kWinding_FillType);
46 path.addRect(10, 10, 30, 30);
47 path.addRect(20, 20, 40, 40);
48 simplify(path, true, out);
49 SkPath expected;
50 expected.setFillType(SkPath::kEvenOdd_FillType);
51 expected.moveTo(10,10); // two cutout corners
52 expected.lineTo(10,30);
53 expected.lineTo(20,30);
54 expected.lineTo(20,40);
55 expected.lineTo(40,40);
56 expected.lineTo(40,20);
57 expected.lineTo(30,20);
58 expected.lineTo(30,10);
59 expected.lineTo(10,10);
60 expected.close();
61 if (out != expected) {
62 SkDebugf("%s expected equal\n", __FUNCTION__);
63 }
64
65 path = out;
66 path.addRect(30, 10, 40, 20);
67 path.addRect(10, 30, 20, 40);
68 simplify(path, true, out);
69 SkRect rect;
70 if (!out.isRect(&rect)) {
71 SkDebugf("%s expected rect\n", __FUNCTION__);
72 }
73 if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
74 SkDebugf("%s expected union\n", __FUNCTION__);
75 }
76
77 path = out;
78 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
79 simplify(path, true, out);
80 if (!out.isEmpty()) {
81 SkDebugf("%s expected empty\n", __FUNCTION__);
82 }
83}
84
85static void testSimplifyAddL() {
86 SkPath path, out;
87 path.moveTo(10,10); // 'L' shape
88 path.lineTo(10,40);
89 path.lineTo(40,40);
90 path.lineTo(40,20);
91 path.lineTo(30,20);
92 path.lineTo(30,10);
93 path.lineTo(10,10);
94 path.close();
95 path.addRect(30, 10, 40, 20); // missing notch of 'L'
96 simplify(path, true, out);
97 SkRect rect;
98 if (!out.isRect(&rect)) {
99 SkDebugf("%s expected rect\n", __FUNCTION__);
100 }
101 if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
102 SkDebugf("%s expected union\n", __FUNCTION__);
103 }
104}
105
106static void testSimplifyCoincidentCCW() {
107 SkPath path, out;
108 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
109 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
110 simplify(path, true, out);
111 SkRect rect;
112 if (!out.isRect(&rect)) {
113 SkDebugf("%s expected rect\n", __FUNCTION__);
114 }
115 if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
116 SkDebugf("%s expected union\n", __FUNCTION__);
117 }
118}
119
120static void testSimplifyCoincidentCW() {
121 SkPath path, out;
122 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
123 path.addRect(10, 10, 40, 40, SkPath::kCW_Direction);
124 simplify(path, true, out);
125 if (!out.isEmpty()) {
126 SkDebugf("%s expected empty\n", __FUNCTION__);
127 }
128}
129
130static void testSimplifyCorner() {
131 SkPath path, out;
132 path.addRect(10, 10, 20, 20, SkPath::kCCW_Direction);
133 path.addRect(20, 20, 40, 40, SkPath::kCW_Direction);
134 simplify(path, true, out);
135 SkTDArray<SkRect> boundsArray;
136 contourBounds(out, boundsArray);
137 if (boundsArray.count() != 2) {
138 SkDebugf("%s expected 2 contours\n", __FUNCTION__);
139 return;
140 }
141 SkRect one = SkRect::MakeLTRB(10, 10, 20, 20);
142 SkRect two = SkRect::MakeLTRB(20, 20, 40, 40);
143 if (boundsArray[0] != one && boundsArray[0] != two
144 || boundsArray[1] != one && boundsArray[1] != two) {
145 SkDebugf("%s expected match\n", __FUNCTION__);
146 }
147}
148
149static void testSimplifyDiagonal() {
150 SkRect rect2 = SkRect::MakeXYWH(10, 10, 10, 10);
151 for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
152 for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
153 for (int x = 0; x <= 20; x += 20) {
154 for (int y = 0; y <= 20; y += 20) {
155 SkPath path, out;
156 SkRect rect1 = SkRect::MakeXYWH(x, y, 10, 10);
157 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
158 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
159 simplify(path, true, out);
160 SkPath::Iter iter(out, false);
161 SkPoint pts[4], lastLine[2];
162 SkPath::Verb verb;
163 SkRect bounds[2];
164 bounds[0].setEmpty();
165 bounds[1].setEmpty();
166 SkRect* boundsPtr = bounds;
167 int count = 0, segments = 0;
168 bool lastLineSet = false;
169 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
170 switch (verb) {
171 case SkPath::kMove_Verb:
172 if (!boundsPtr->isEmpty()) {
173 SkASSERT(boundsPtr == bounds);
174 ++boundsPtr;
175 }
176 boundsPtr->set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
177 count = 0;
178 lastLineSet = false;
179 break;
180 case SkPath::kLine_Verb:
181 if (lastLineSet) {
182 SkASSERT((lastLine[1].fX - lastLine[0].fX) *
183 (pts[1].fY - lastLine[0].fY) !=
184 (lastLine[1].fY - lastLine[0].fY) *
185 (pts[1].fX - lastLine[0].fX));
186 }
187 lastLineSet = true;
188 lastLine[0] = pts[0];
189 lastLine[1] = pts[1];
190 count = 1;
191 ++segments;
192 break;
193 case SkPath::kClose_Verb:
194 count = 0;
195 break;
196 default:
197 SkDEBUGFAIL("bad verb");
198 return;
199 }
200 for (int i = 1; i <= count; ++i) {
201 boundsPtr->growToInclude(pts[i].fX, pts[i].fY);
202 }
203 }
204 if (boundsPtr != bounds) {
205 SkASSERT((bounds[0] == rect1 || bounds[1] == rect1)
206 && (bounds[0] == rect2 || bounds[1] == rect2));
207 } else {
208 SkASSERT(segments == 8);
209 }
210 }
211 }
212 }
213 }
214}
215
216static void assertOneContour(const SkPath& out, bool edge, bool extend) {
217 SkPath::Iter iter(out, false);
218 SkPoint pts[4];
219 SkPath::Verb verb;
220 SkRect bounds;
221 bounds.setEmpty();
222 int count = 0;
223 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
224 switch (verb) {
225 case SkPath::kMove_Verb:
226 SkASSERT(count == 0);
227 break;
228 case SkPath::kLine_Verb:
229 SkASSERT(pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY);
230 ++count;
231 break;
232 case SkPath::kClose_Verb:
233 break;
234 default:
235 SkDEBUGFAIL("bad verb");
236 return;
237 }
238 }
239 SkASSERT(count == (extend ? 4 : edge ? 6 : 8));
240}
241
242static void testSimplifyCoincident() {
243 // outside to inside, outside to right, outside to outside
244 // left to inside, left to right, left to outside
245 // inside to right, inside to outside
246 // repeat above for left, right, bottom
247 SkScalar start[] = { 0, 10, 20 };
248 size_t startCount = sizeof(start) / sizeof(start[0]);
249 SkScalar stop[] = { 30, 40, 50 };
250 size_t stopCount = sizeof(stop) / sizeof(stop[0]);
251 SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
252 for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
253 for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
254 for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
255 for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
256 bool extend = start[startIndex] == rect2.fLeft && stop[stopIndex] == rect2.fRight;
257 bool edge = start[startIndex] == rect2.fLeft || stop[stopIndex] == rect2.fRight;
258 SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 10);
259 SkPath path, out;
260 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
261 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
262 simplify(path, true, out);
263 assertOneContour(out, edge, extend);
264
265 path.reset();
266 rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 50);
267 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
268 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
269 simplify(path, true, out);
270 assertOneContour(out, edge, extend);
271
272 path.reset();
273 rect1 = SkRect::MakeLTRB(0, start[startIndex], 10, stop[stopIndex]);
274 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
275 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
276 simplify(path, true, out);
277 assertOneContour(out, edge, extend);
278
279 path.reset();
280 rect1 = SkRect::MakeLTRB(40, start[startIndex], 50, stop[stopIndex]);
281 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
282 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
283 simplify(path, true, out);
284 assertOneContour(out, edge, extend);
285 }
286 }
287 }
288 }
289}
290
291static void testSimplifyOverlap() {
292 SkScalar start[] = { 0, 10, 20 };
293 size_t startCount = sizeof(start) / sizeof(start[0]);
294 SkScalar stop[] = { 30, 40, 50 };
295 size_t stopCount = sizeof(stop) / sizeof(stop[0]);
296 SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
297 for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
298 for (size_t lefty = 0; lefty < startCount; ++lefty) {
299 for (size_t righty = 0; righty < stopCount; ++righty) {
300 for (size_t toppy = 0; toppy < startCount; ++toppy) {
301 for (size_t botty = 0; botty < stopCount; ++botty) {
302 SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
303 stop[righty], stop[botty]);
304 SkPath path, out;
305 path.addRect(rect1, static_cast<SkPath::Direction>(dir));
306 path.addRect(rect2, static_cast<SkPath::Direction>(dir));
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000307 testSimplify(path, true, out);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000308 }
309 }
310 }
311 }
312 }
313}
314
315static void testSimplifyOverlapTiny() {
316 SkScalar start[] = { 0, 1, 2 };
317 size_t startCount = sizeof(start) / sizeof(start[0]);
318 SkScalar stop[] = { 3, 4, 5 };
319 size_t stopCount = sizeof(stop) / sizeof(stop[0]);
320 SkRect rect2 = SkRect::MakeXYWH(1, 1, 3, 3);
321 for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
322 for (size_t lefty = 0; lefty < startCount; ++lefty) {
323 for (size_t righty = 0; righty < stopCount; ++righty) {
324 for (size_t toppy = 0; toppy < startCount; ++toppy) {
325 for (size_t botty = 0; botty < stopCount; ++botty) {
326 SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
327 stop[righty], stop[botty]);
328 SkPath path, out;
329 path.addRect(rect1, static_cast<SkPath::Direction>(dir));
330 path.addRect(rect2, static_cast<SkPath::Direction>(dir));
331 simplify(path, true, out);
332 comparePathsTiny(path, out);
333 }
334 }
335 }
336 }
337 }
338}
339
340static void testSimplifyDegenerate() {
341 SkScalar start[] = { 0, 10, 20 };
342 size_t startCount = sizeof(start) / sizeof(start[0]);
343 SkScalar stop[] = { 30, 40, 50 };
344 size_t stopCount = sizeof(stop) / sizeof(stop[0]);
345 SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
346 for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
347 for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
348 for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
349 for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
350 SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 0);
351 SkPath path, out;
352 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
353 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
354 simplify(path, true, out);
355 SkRect rect;
356 if (!out.isRect(&rect)) {
357 SkDebugf("%s 1 expected rect\n", __FUNCTION__);
358 }
359 if (rect != rect2) {
360 SkDebugf("%s 1 expected union\n", __FUNCTION__);
361 }
362
363 path.reset();
364 rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 40);
365 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
366 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
367 simplify(path, true, out);
368 if (!out.isRect(&rect)) {
369 SkDebugf("%s 2 expected rect\n", __FUNCTION__);
370 }
371 if (rect != rect2) {
372 SkDebugf("%s 2 expected union\n", __FUNCTION__);
373 }
374
375 path.reset();
376 rect1 = SkRect::MakeLTRB(0, start[startIndex], 0, stop[stopIndex]);
377 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
378 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
379 simplify(path, true, out);
380 if (!out.isRect(&rect)) {
381 SkDebugf("%s 3 expected rect\n", __FUNCTION__);
382 }
383 if (rect != rect2) {
384 SkDebugf("%s 3 expected union\n", __FUNCTION__);
385 }
386
387 path.reset();
388 rect1 = SkRect::MakeLTRB(40, start[startIndex], 40, stop[stopIndex]);
389 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
390 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
391 simplify(path, true, out);
392 if (!out.isRect(&rect)) {
393 SkDebugf("%s 4 expected rect\n", __FUNCTION__);
394 }
395 if (rect != rect2) {
396 SkDebugf("%s 4 expected union\n", __FUNCTION__);
397 }
398 }
399 }
400 }
401 }
402}
403
404static void testSimplifyDegenerate1() {
405 SkPath path, out;
406 path.setFillType(SkPath::kWinding_FillType);
407 path.addRect( 0, 0, 0, 30);
408 path.addRect(10, 10, 40, 40);
409 simplify(path, true, out);
410 SkRect rect;
411 if (!out.isRect(&rect)) {
412 SkDebugf("%s expected rect\n", __FUNCTION__);
413 }
414 if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
415 SkDebugf("%s expected union\n", __FUNCTION__);
416 }
417}
418
419static void (*simplifyTests[])() = {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000420 testSimplifyCoincidentInner,
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000421 testSimplifyOverlapTiny,
422 testSimplifyDegenerate1,
423 testSimplifyCorner,
424 testSimplifyDegenerate,
425 testSimplifyOverlap,
426 testSimplifyDiagonal,
427 testSimplifyCoincident,
428 testSimplifyCoincidentCW,
429 testSimplifyCoincidentCCW,
430 testSimplifyCoincidentVertical,
431 testSimplifyCoincidentHorizontal,
432 testSimplifyAddL,
433 testSimplifyMulti,
434};
435
436static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
437
438static void (*firstTest)() = 0;
439
440void SimplifyRectangularPaths_Test() {
441 size_t index = 0;
442 if (firstTest) {
443 while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
444 ++index;
445 }
446 }
447 for ( ; index < simplifyTestsCount; ++index) {
448 if (simplifyTests[index] == testSimplifyCorner) {
449 // testSimplifyCorner fails because it expects two contours, where
450 // only one is returned. Both results are reasonable, but if two
451 // contours are desirable, or if we provide an option to choose
452 // between longer contours and more contours, turn this back on. For
453 // the moment, testSimplifyDiagonal also checks the test case, and
454 // permits either two rects or one non-crossing poly as valid
455 // unreported results.
456 continue;
457 }
458 (*simplifyTests[index])();
459 }
460}
461