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