blob: 44a313b28570595e82ea092ed97ef15a34fc26fd [file] [log] [blame]
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001/*
2 * Copyright 2018 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 */
7
8#include "gm.h"
9#include "sk_tool_utils.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040010#include "SkPolyUtils.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040011#include "SkPathPriv.h"
12
13static void create_ngon(int n, SkPoint* pts, SkScalar w, SkScalar h, SkPath::Direction dir) {
14 float angleStep = 360.0f / n, angle = 0.0f, sin, cos;
15 if ((n % 2) == 1) {
16 angle = angleStep/2.0f;
17 }
18 if (SkPath::kCCW_Direction == dir) {
19 angle = -angle;
20 angleStep = -angleStep;
21 }
22
23 for (int i = 0; i < n; ++i) {
24 sin = SkScalarSinCos(SkDegreesToRadians(angle), &cos);
25 pts[i].fX = -sin * w;
26 pts[i].fY = cos * h;
27 angle += angleStep;
28 }
29}
30
31namespace PolygonOffsetData {
32// narrow rect
33const SkPoint gPoints0[] = {
34 { -1.5f, -50.0f },
35 { 1.5f, -50.0f },
36 { 1.5f, 50.0f },
37 { -1.5f, 50.0f }
38};
39// narrow rect on an angle
40const SkPoint gPoints1[] = {
41 { -50.0f, -49.0f },
42 { -49.0f, -50.0f },
43 { 50.0f, 49.0f },
44 { 49.0f, 50.0f }
45};
46// trap - narrow on top - wide on bottom
47const SkPoint gPoints2[] = {
48 { -10.0f, -50.0f },
49 { 10.0f, -50.0f },
50 { 50.0f, 50.0f },
51 { -50.0f, 50.0f }
52};
53// wide skewed rect
54const SkPoint gPoints3[] = {
55 { -50.0f, -50.0f },
56 { 0.0f, -50.0f },
57 { 50.0f, 50.0f },
58 { 0.0f, 50.0f }
59};
60// thin rect with colinear-ish lines
61const SkPoint gPoints4[] = {
62 { -6.0f, -50.0f },
63 { 4.0f, -50.0f },
64 { 5.0f, -25.0f },
65 { 6.0f, 0.0f },
66 { 5.0f, 25.0f },
67 { 4.0f, 50.0f },
68 { -4.0f, 50.0f }
69};
70// degenerate
71const SkPoint gPoints5[] = {
72 { -0.025f, -0.025f },
73 { 0.025f, -0.025f },
74 { 0.025f, 0.025f },
75 { -0.025f, 0.025f }
76};
77// Quad with near coincident point
78const SkPoint gPoints6[] = {
79 { -20.0f, -13.0f },
80 { -20.0f, -13.05f },
81 { 20.0f, -13.0f },
82 { 20.0f, 27.0f }
83};
84// thin rect with colinear lines
85const SkPoint gPoints7[] = {
86 { -10.0f, -50.0f },
87 { 10.0f, -50.0f },
88 { 10.0f, -25.0f },
89 { 10.0f, 0.0f },
90 { 10.0f, 25.0f },
91 { 10.0f, 50.0f },
92 { -10.0f, 50.0f }
93};
94// capped teardrop
95const SkPoint gPoints8[] = {
96 { 50.00f, 50.00f },
97 { 0.00f, 50.00f },
98 { -15.45f, 47.55f },
99 { -29.39f, 40.45f },
100 { -40.45f, 29.39f },
101 { -47.55f, 15.45f },
102 { -50.00f, 0.00f },
103 { -47.55f, -15.45f },
104 { -40.45f, -29.39f },
105 { -29.39f, -40.45f },
106 { -15.45f, -47.55f },
107 { 0.00f, -50.00f },
108 { 50.00f, -50.00f }
109};
110// teardrop
111const SkPoint gPoints9[] = {
112 { 4.39f, 40.45f },
113 { -9.55f, 47.55f },
114 { -25.00f, 50.00f },
115 { -40.45f, 47.55f },
116 { -54.39f, 40.45f },
117 { -65.45f, 29.39f },
118 { -72.55f, 15.45f },
119 { -75.00f, 0.00f },
120 { -72.55f, -15.45f },
121 { -65.45f, -29.39f },
122 { -54.39f, -40.45f },
123 { -40.45f, -47.55f },
124 { -25.0f, -50.0f },
125 { -9.55f, -47.55f },
126 { 4.39f, -40.45f },
127 { 75.00f, 0.00f }
128};
129// clipped triangle
130const SkPoint gPoints10[] = {
131 { -10.0f, -50.0f },
132 { 10.0f, -50.0f },
133 { 50.0f, 31.0f },
134 { 40.0f, 50.0f },
135 { -40.0f, 50.0f },
136 { -50.0f, 31.0f },
137};
138
139// tab
140const SkPoint gPoints11[] = {
141 { -45, -25 },
142 { 45, -25 },
143 { 45, 25 },
144 { 20, 25 },
145 { 19.6157f, 25.f + 3.9018f },
146 { 18.4776f, 25.f + 7.6537f },
147 { 16.6294f, 25.f + 11.1114f },
148 { 14.1421f, 25.f + 14.1421f },
149 { 11.1114f, 25.f + 16.6294f },
150 { 7.6537f, 25.f + 18.4776f },
151 { 3.9018f, 25.f + 19.6157f },
152 { 0, 45.f },
153 { -3.9018f, 25.f + 19.6157f },
154 { -7.6537f, 25.f + 18.4776f },
155 { -11.1114f, 25.f + 16.6294f },
156 { -14.1421f, 25.f + 14.1421f },
157 { -16.6294f, 25.f + 11.1114f },
158 { -18.4776f, 25.f + 7.6537f },
159 { -19.6157f, 25.f + 3.9018f },
160 { -20, 25 },
161 { -45, 25 }
162};
163
164// star of david
165const SkPoint gPoints12[] = {
166 { 0.0f, -50.0f },
167 { 14.43f, -25.0f },
168 { 43.30f, -25.0f },
169 { 28.86f, 0.0f },
170 { 43.30f, 25.0f },
171 { 14.43f, 25.0f },
172 { 0.0f, 50.0f },
173 { -14.43f, 25.0f },
174 { -43.30f, 25.0f },
175 { -28.86f, 0.0f },
176 { -43.30f, -25.0f },
177 { -14.43f, -25.0f },
178};
179
180// notch
181const SkScalar kBottom = 25.f;
182const SkPoint gPoints13[] = {
183 { -50, kBottom - 50.f },
184 { 50, kBottom - 50.f },
185 { 50, kBottom },
186 { 20, kBottom },
187 { 19.6157f, kBottom - 3.9018f },
188 { 18.4776f, kBottom - 7.6537f },
189 { 16.6294f, kBottom - 11.1114f },
190 { 14.1421f, kBottom - 14.1421f },
191 { 11.1114f, kBottom - 16.6294f },
192 { 7.6537f, kBottom - 18.4776f },
193 { 3.9018f, kBottom - 19.6157f },
194 { 0, kBottom - 20.f },
195 { -3.9018f, kBottom - 19.6157f },
196 { -7.6537f, kBottom - 18.4776f },
197 { -11.1114f, kBottom - 16.6294f },
198 { -14.1421f, kBottom - 14.1421f },
199 { -16.6294f, kBottom - 11.1114f },
200 { -18.4776f, kBottom - 7.6537f },
201 { -19.6157f, kBottom - 3.9018f },
202 { -20, kBottom },
203 { -50, kBottom }
204};
205
206// crown
207const SkPoint gPoints14[] = {
208 { -40, -39 },
209 { 40, -39 },
210 { 40, -20 },
211 { 30, 40 },
212 { 20, -20 },
213 { 10, 40 },
214 { 0, -20 },
215 { -10, 40 },
216 { -20, -20 },
217 { -30, 40 },
218 { -40, -20 }
219};
220
221// dumbbell
222const SkPoint gPoints15[] = {
223 { -26, -3 },
224 { -24, -6.2f },
225 { -22.5f, -8 },
226 { -20, -9.9f },
227 { -17.5f, -10.3f },
228 { -15, -10.9f },
229 { -12.5f, -10.2f },
230 { -10, -9.7f },
231 { -7.5f, -8.1f },
232 { -5, -7.7f },
233 { -2.5f, -7.4f },
234 { 0, -7.7f },
235 { 3, -9 },
236 { 6.5f, -11.5f },
237 { 10.6f, -14 },
238 { 14, -15.2f },
239 { 17, -15.5f },
240 { 20, -15.2f },
241 { 23.4f, -14 },
242 { 27.5f, -11.5f },
243 { 30, -8 },
244 { 32, -4 },
245 { 32.5f, 0 },
246 { 32, 4 },
247 { 30, 8 },
248 { 27.5f, 11.5f },
249 { 23.4f, 14 },
250 { 20, 15.2f },
251 { 17, 15.5f },
252 { 14, 15.2f },
253 { 10.6f, 14 },
254 { 6.5f, 11.5f },
255 { 3, 9 },
256 { 0, 7.7f },
257 { -2.5f, 7.4f },
258 { -5, 7.7f },
259 { -7.5f, 8.1f },
260 { -10, 9.7f },
261 { -12.5f, 10.2f },
262 { -15, 10.9f },
263 { -17.5f, 10.3f },
264 { -20, 9.9f },
265 { -22.5f, 8 },
266 { -24, 6.2f },
267 { -26, 3 },
268 { -26.5f, 0 }
269};
270
271// truncated dumbbell
272// (checks winding computation in OffsetSimplePolygon)
273const SkPoint gPoints16[] = {
274 { -15 + 3, -9 },
275 { -15 + 6.5f, -11.5f },
276 { -15 + 10.6f, -14 },
277 { -15 + 14, -15.2f },
278 { -15 + 17, -15.5f },
279 { -15 + 20, -15.2f },
280 { -15 + 23.4f, -14 },
281 { -15 + 27.5f, -11.5f },
282 { -15 + 30, -8 },
283 { -15 + 32, -4 },
284 { -15 + 32.5f, 0 },
285 { -15 + 32, 4 },
286 { -15 + 30, 8 },
287 { -15 + 27.5f, 11.5f },
288 { -15 + 23.4f, 14 },
289 { -15 + 20, 15.2f },
290 { -15 + 17, 15.5f },
291 { -15 + 14, 15.2f },
292 { -15 + 10.6f, 14 },
293 { -15 + 6.5f, 11.5f },
294 { -15 + 3, 9 },
295};
296
297// square notch
298// (to detect segment-segment intersection)
299const SkPoint gPoints17[] = {
300 { -50, kBottom - 50.f },
301 { 50, kBottom - 50.f },
302 { 50, kBottom },
303 { 20, kBottom },
304 { 20, kBottom - 20.f },
305 { -20, kBottom - 20.f },
306 { -20, kBottom },
307 { -50, kBottom }
308};
309
310// box with Peano curve
311const SkPoint gPoints18[] = {
312 { 0, 0 },
313 { 0, -12 },
314 { -6, -12 },
315 { -6, 0 },
316 { -12, 0 },
317 { -12, -12},
318 { -18, -12},
319 { -18, 18},
320 { -12, 18},
321 {-12, 6},
322 {-6, 6},
323 {-6, 36},
324 {-12, 36},
325 {-12, 24},
326 {-18, 24},
327 {-18, 36},
328 {-24, 36},
329 {-24, 24},
330 {-30, 24},
331 {-30, 36},
332 {-36, 36},
333 {-36, 6},
334 {-30, 6},
335 {-30, 18},
336 {-24, 18},
337 {-24, -12},
338 {-30, -12},
339 {-30, 0},
340 {-36, 0},
341 {-36, -36},
342 {36, -36},
343 {36, 36},
344 {12, 36},
345 {12, 24},
346 {6, 24},
347 {6, 36},
348 {0, 36},
349 {0, 6},
350 {6, 6},
351 {6, 18},
352 {12, 18},
353 {12, -12},
354 {6, -12},
355 {6, 0}
356};
357
358
359const SkPoint* gConvexPoints[] = {
360 gPoints0, gPoints1, gPoints2, gPoints3, gPoints4, gPoints5, gPoints6,
361 gPoints7, gPoints8, gPoints9, gPoints10,
362};
363
364const size_t gConvexSizes[] = {
365 SK_ARRAY_COUNT(gPoints0),
366 SK_ARRAY_COUNT(gPoints1),
367 SK_ARRAY_COUNT(gPoints2),
368 SK_ARRAY_COUNT(gPoints3),
369 SK_ARRAY_COUNT(gPoints4),
370 SK_ARRAY_COUNT(gPoints5),
371 SK_ARRAY_COUNT(gPoints6),
372 SK_ARRAY_COUNT(gPoints7),
373 SK_ARRAY_COUNT(gPoints8),
374 SK_ARRAY_COUNT(gPoints9),
375 SK_ARRAY_COUNT(gPoints10),
376};
377static_assert(SK_ARRAY_COUNT(gConvexSizes) == SK_ARRAY_COUNT(gConvexPoints), "array_mismatch");
378
379const SkPoint* gSimplePoints[] = {
380 gPoints0, gPoints1, gPoints2, gPoints4, gPoints5, gPoints7,
381 gPoints8, gPoints11, gPoints12, gPoints13, gPoints14, gPoints15,
382 gPoints16, gPoints17, gPoints18,
383};
384
385const size_t gSimpleSizes[] = {
386 SK_ARRAY_COUNT(gPoints0),
387 SK_ARRAY_COUNT(gPoints1),
388 SK_ARRAY_COUNT(gPoints2),
389 SK_ARRAY_COUNT(gPoints4),
390 SK_ARRAY_COUNT(gPoints5),
391 SK_ARRAY_COUNT(gPoints7),
392 SK_ARRAY_COUNT(gPoints8),
393 SK_ARRAY_COUNT(gPoints11),
394 SK_ARRAY_COUNT(gPoints12),
395 SK_ARRAY_COUNT(gPoints13),
396 SK_ARRAY_COUNT(gPoints14),
397 SK_ARRAY_COUNT(gPoints15),
398 SK_ARRAY_COUNT(gPoints16),
399 SK_ARRAY_COUNT(gPoints17),
400 SK_ARRAY_COUNT(gPoints18),
401};
402static_assert(SK_ARRAY_COUNT(gSimpleSizes) == SK_ARRAY_COUNT(gSimplePoints), "array_mismatch");
403
404}
405
406namespace skiagm {
407
408// This GM is intended to exercise the offsetting of polygons
Jim Van Verthbdde4282018-06-14 09:09:18 -0400409// When fVariableOffset is true it will skew the offset by x,
410// to test perspective and other variable offset functions
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400411class PolygonOffsetGM : public GM {
412public:
Jim Van Verthbdde4282018-06-14 09:09:18 -0400413 PolygonOffsetGM(bool convexOnly, bool variableOffset)
414 : fConvexOnly(convexOnly)
415 , fVariableOffset(variableOffset) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400416 this->setBGColor(0xFFFFFFFF);
417 }
418
419protected:
420 SkString onShortName() override {
421 if (fConvexOnly) {
Jim Van Verthbdde4282018-06-14 09:09:18 -0400422 if (fVariableOffset) {
423 return SkString("convex-polygon-inset-v");
424 } else {
425 return SkString("convex-polygon-inset");
426 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400427 } else {
Jim Van Verthbdde4282018-06-14 09:09:18 -0400428 if (fVariableOffset) {
429 return SkString("simple-polygon-offset-v");
430 } else {
431 return SkString("simple-polygon-offset");
432 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400433 }
434 }
435 SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
436 bool runAsBench() const override { return true; }
437
438 static void GetConvexPolygon(int index, SkPath::Direction dir,
439 std::unique_ptr<SkPoint[]>* data, int* numPts) {
440 if (index < (int)SK_ARRAY_COUNT(PolygonOffsetData::gConvexPoints)) {
441 // manually specified
442 *numPts = (int)PolygonOffsetData::gConvexSizes[index];
443 data->reset(new SkPoint[*numPts]);
444 if (SkPath::kCW_Direction == dir) {
445 for (int i = 0; i < *numPts; ++i) {
446 (*data)[i] = PolygonOffsetData::gConvexPoints[index][i];
447 }
448 } else {
449 for (int i = 0; i < *numPts; ++i) {
450 (*data)[i] = PolygonOffsetData::gConvexPoints[index][*numPts - i - 1];
451 }
452 }
453 } else {
454 // procedurally generated
455 SkScalar width = kMaxPathHeight / 2;
456 SkScalar height = kMaxPathHeight / 2;
457 int numPtsArray[] = { 3, 4, 5, 5, 6, 8, 8, 20, 100 };
458
459 size_t arrayIndex = index - SK_ARRAY_COUNT(PolygonOffsetData::gConvexPoints);
460 SkASSERT(arrayIndex < SK_ARRAY_COUNT(numPtsArray));
461 *numPts = numPtsArray[arrayIndex];
462 if (arrayIndex == 3 || arrayIndex == 6) {
463 // squashed pentagon and octagon
464 width = kMaxPathHeight / 5;
465 }
466
467 data->reset(new SkPoint[*numPts]);
468
469 create_ngon(*numPts, data->get(), width, height, dir);
470 }
471 }
472
473 static void GetSimplePolygon(int index, SkPath::Direction dir,
474 std::unique_ptr<SkPoint[]>* data, int* numPts) {
475 if (index < (int)SK_ARRAY_COUNT(PolygonOffsetData::gSimplePoints)) {
476 // manually specified
477 *numPts = (int)PolygonOffsetData::gSimpleSizes[index];
478 data->reset(new SkPoint[*numPts]);
479 if (SkPath::kCW_Direction == dir) {
480 for (int i = 0; i < *numPts; ++i) {
481 (*data)[i] = PolygonOffsetData::gSimplePoints[index][i];
482 }
483 } else {
484 for (int i = 0; i < *numPts; ++i) {
485 (*data)[i] = PolygonOffsetData::gSimplePoints[index][*numPts - i - 1];
486 }
487 }
488 } else {
489 // procedurally generated
490 SkScalar width = kMaxPathHeight / 2;
491 SkScalar height = kMaxPathHeight / 2;
492 int numPtsArray[] = { 5, 7, 8, 20, 100 };
493
494 size_t arrayIndex = index - SK_ARRAY_COUNT(PolygonOffsetData::gSimplePoints);
495 arrayIndex = SkTMin(arrayIndex, SK_ARRAY_COUNT(numPtsArray) - 1);
496 SkASSERT(arrayIndex < SK_ARRAY_COUNT(numPtsArray));
497 *numPts = numPtsArray[arrayIndex];
498 // squash horizontally
499 width = kMaxPathHeight / 5;
500
501 data->reset(new SkPoint[*numPts]);
502
503 create_ngon(*numPts, data->get(), width, height, dir);
504 }
505 }
506 // Draw a single polygon with insets and potentially outsets
507 void drawPolygon(SkCanvas* canvas, int index, SkPoint* offset) {
508
509 SkPoint center;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400510 SkRect bounds;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400511 {
512 std::unique_ptr<SkPoint[]> data(nullptr);
513 int numPts;
514 if (fConvexOnly) {
515 GetConvexPolygon(index, SkPath::kCW_Direction, &data, &numPts);
516 } else {
517 GetSimplePolygon(index, SkPath::kCW_Direction, &data, &numPts);
518 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400519 bounds.set(data.get(), numPts);
520 if (!fConvexOnly) {
521 bounds.outset(kMaxOutset, kMaxOutset);
522 }
523 if (offset->fX + bounds.width() > kGMWidth) {
524 offset->fX = 0;
525 offset->fY += kMaxPathHeight;
526 }
527 center = { offset->fX + SkScalarHalf(bounds.width()), offset->fY };
528 offset->fX += bounds.width();
529 }
530
531 const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
532 const float insets[] = { 5, 10, 15, 20, 25, 30, 35, 40 };
533 const float offsets[] = { 2, 5, 9, 14, 20, 27, 35, 44, -2, -5, -9 };
534 const SkColor colors[] = { 0xFF901313, 0xFF8D6214, 0xFF698B14, 0xFF1C8914,
535 0xFF148755, 0xFF146C84, 0xFF142482, 0xFF4A1480,
536 0xFF901313, 0xFF8D6214, 0xFF698B14 };
537
538 SkPaint paint;
539 paint.setAntiAlias(true);
540 paint.setStyle(SkPaint::kStroke_Style);
541 paint.setStrokeWidth(1);
542
543 std::unique_ptr<SkPoint[]> data(nullptr);
544 int numPts;
545 if (fConvexOnly) {
546 GetConvexPolygon(index, dirs[index % 2], &data, &numPts);
547 } else {
548 GetSimplePolygon(index, dirs[index % 2], &data, &numPts);
549 }
550
551 {
552 SkPath path;
553 path.moveTo(data.get()[0]);
554 for (int i = 1; i < numPts; ++i) {
555 path.lineTo(data.get()[i]);
556 }
557 path.close();
558 canvas->save();
559 canvas->translate(center.fX, center.fY);
560 canvas->drawPath(path, paint);
561 canvas->restore();
562 }
563
564 SkTDArray<SkPoint> offsetPoly;
565 size_t count = fConvexOnly ? SK_ARRAY_COUNT(insets) : SK_ARRAY_COUNT(offsets);
Jim Van Verthbdde4282018-06-14 09:09:18 -0400566 SkScalar localCenterX = bounds.centerX();
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400567 for (size_t i = 0; i < count; ++i) {
Jim Van Verthbdde4282018-06-14 09:09:18 -0400568 SkScalar offset = fConvexOnly ? insets[i] : offsets[i];
569 std::function<SkScalar(const SkPoint&)> offsetFunc;
570 if (fVariableOffset) {
571 offsetFunc = [offset, localCenterX](const SkPoint& p) {
572 return offset + 0.04f*(p.fX - localCenterX);
573 };
574 } else {
575 offsetFunc = [offset](const SkPoint& p) {
576 return offset;
577 };
578 }
579
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400580 bool result;
581 if (fConvexOnly) {
Jim Van Verthbdde4282018-06-14 09:09:18 -0400582 result = SkInsetConvexPolygon(data.get(), numPts, offsetFunc, &offsetPoly);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400583 } else {
Jim Van Verthbdde4282018-06-14 09:09:18 -0400584 result = SkOffsetSimplePolygon(data.get(), numPts, offsetFunc, &offsetPoly);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400585 }
586 if (result) {
587 SkPath path;
588 path.moveTo(offsetPoly[0]);
589 for (int i = 1; i < offsetPoly.count(); ++i) {
590 path.lineTo(offsetPoly[i]);
591 }
592 path.close();
593
594 paint.setColor(sk_tool_utils::color_to_565(colors[i]));
595 canvas->save();
596 canvas->translate(center.fX, center.fY);
597 canvas->drawPath(path, paint);
598 canvas->restore();
599 }
600 }
601 }
602
603 void onDraw(SkCanvas* canvas) override {
604 // the right edge of the last drawn path
605 SkPoint offset = { 0, SkScalarHalf(kMaxPathHeight) };
606 if (!fConvexOnly) {
607 offset.fY += kMaxOutset;
608 }
609
610 for (int i = 0; i < kNumPaths; ++i) {
611 this->drawPolygon(canvas, i, &offset);
612 }
613 }
614
615private:
616 static constexpr int kNumPaths = 20;
617 static constexpr int kMaxPathHeight = 100;
618 static constexpr int kMaxOutset = 16;
619 static constexpr int kGMWidth = 512;
620 static constexpr int kGMHeight = 512;
621
622 bool fConvexOnly;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400623 bool fVariableOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400624
625 typedef GM INHERITED;
626};
627
628//////////////////////////////////////////////////////////////////////////////
629
Jim Van Verthbdde4282018-06-14 09:09:18 -0400630DEF_GM(return new PolygonOffsetGM(true, false);)
631DEF_GM(return new PolygonOffsetGM(true, true);)
632DEF_GM(return new PolygonOffsetGM(false, false);)
633DEF_GM(return new PolygonOffsetGM(false, true);)
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400634}