blob: e323f21762583466dd2e44c75d6e7aa323c0a14c [file] [log] [blame]
Brian Salomonab664fa2017-03-24 16:07:20 +00001/*
2 * Copyright 2017 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
Jim Van Verth8664a1d2018-06-28 16:26:50 -04008#include "SkPolyUtils.h"
Brian Salomonab664fa2017-03-24 16:07:20 +00009
Cary Clarkdf429f32017-11-08 11:44:31 -050010#include "SkPointPriv.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040011#include "SkTArray.h"
Brian Salomonab664fa2017-03-24 16:07:20 +000012#include "SkTemplates.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040013#include "SkTDPQueue.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040014#include "SkTInternalLList.h"
15
16//////////////////////////////////////////////////////////////////////////////////
17// Helper data structures and functions
Brian Salomonab664fa2017-03-24 16:07:20 +000018
Jim Van Verth4db18ed2018-04-03 10:00:37 -040019struct OffsetSegment {
Brian Salomonab664fa2017-03-24 16:07:20 +000020 SkPoint fP0;
21 SkPoint fP1;
22};
23
24// Computes perpDot for point compared to segment.
25// A positive value means the point is to the left of the segment,
26// negative is to the right, 0 is collinear.
27static int compute_side(const SkPoint& s0, const SkPoint& s1, const SkPoint& p) {
28 SkVector v0 = s1 - s0;
29 SkVector v1 = p - s0;
30 SkScalar perpDot = v0.cross(v1);
31 if (!SkScalarNearlyZero(perpDot)) {
32 return ((perpDot > 0) ? 1 : -1);
33 }
34
35 return 0;
36}
37
Jim Van Verth6784ffa2018-07-03 16:12:39 -040038// Returns 1 for cw, -1 for ccw and 0 if zero signed area (either degenerate or self-intersecting)
39int SkGetPolygonWinding(const SkPoint* polygonVerts, int polygonSize) {
40 if (polygonSize < 3) {
41 return 0;
42 }
43
Jim Van Verth8664a1d2018-06-28 16:26:50 -040044 // compute area and use sign to determine winding
45 SkScalar quadArea = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040046 SkVector v0 = polygonVerts[1] - polygonVerts[0];
47 for (int curr = 1; curr < polygonSize - 1; ++curr) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040048 int next = (curr + 1) % polygonSize;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040049 SkVector v1 = polygonVerts[next] - polygonVerts[0];
50 quadArea += v0.cross(v1);
51 v0 = v1;
Brian Salomonab664fa2017-03-24 16:07:20 +000052 }
Jim Van Verth8664a1d2018-06-28 16:26:50 -040053 if (SkScalarNearlyZero(quadArea)) {
54 return 0;
55 }
56 // 1 == ccw, -1 == cw
57 return (quadArea > 0) ? 1 : -1;
Brian Salomonab664fa2017-03-24 16:07:20 +000058}
59
Jim Van Verthbdde4282018-06-14 09:09:18 -040060// Helper function to compute the individual vector for non-equal offsets
61inline void compute_offset(SkScalar d, const SkPoint& polyPoint, int side,
62 const SkPoint& outerTangentIntersect, SkVector* v) {
63 SkScalar dsq = d * d;
64 SkVector dP = outerTangentIntersect - polyPoint;
65 SkScalar dPlenSq = SkPointPriv::LengthSqd(dP);
66 if (SkScalarNearlyZero(dPlenSq)) {
67 v->set(0, 0);
68 } else {
69 SkScalar discrim = SkScalarSqrt(dPlenSq - dsq);
70 v->fX = (dsq*dP.fX - side * d*dP.fY*discrim) / dPlenSq;
71 v->fY = (dsq*dP.fY + side * d*dP.fX*discrim) / dPlenSq;
72 }
73}
74
75// Compute difference vector to offset p0-p1 'd0' and 'd1' units in direction specified by 'side'
76bool compute_offset_vectors(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
77 int side, SkPoint* vector0, SkPoint* vector1) {
Jim Van Verthda965502017-04-11 15:29:14 -040078 SkASSERT(side == -1 || side == 1);
Jim Van Verthda965502017-04-11 15:29:14 -040079 if (SkScalarNearlyEqual(d0, d1)) {
80 // if distances are equal, can just outset by the perpendicular
Jim Van Verthbdde4282018-06-14 09:09:18 -040081 SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX);
Jim Van Verthda965502017-04-11 15:29:14 -040082 perp.setLength(d0*side);
Jim Van Verthbdde4282018-06-14 09:09:18 -040083 *vector0 = perp;
84 *vector1 = perp;
Jim Van Verthda965502017-04-11 15:29:14 -040085 } else {
Jim Van Verthbdde4282018-06-14 09:09:18 -040086 SkScalar d0abs = SkTAbs(d0);
87 SkScalar d1abs = SkTAbs(d1);
Jim Van Verthda965502017-04-11 15:29:14 -040088 // Otherwise we need to compute the outer tangent.
89 // See: http://www.ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm
Jim Van Verthbdde4282018-06-14 09:09:18 -040090 if (d0abs < d1abs) {
Jim Van Verthda965502017-04-11 15:29:14 -040091 side = -side;
92 }
Jim Van Verthbdde4282018-06-14 09:09:18 -040093 SkScalar dD = d0abs - d1abs;
Jim Van Verthda965502017-04-11 15:29:14 -040094 // if one circle is inside another, we can't compute an offset
Cary Clarkdf429f32017-11-08 11:44:31 -050095 if (dD*dD >= SkPointPriv::DistanceToSqd(p0, p1)) {
Jim Van Verthda965502017-04-11 15:29:14 -040096 return false;
97 }
Jim Van Verthbdde4282018-06-14 09:09:18 -040098 SkPoint outerTangentIntersect = SkPoint::Make((p1.fX*d0abs - p0.fX*d1abs) / dD,
99 (p1.fY*d0abs - p0.fY*d1abs) / dD);
Jim Van Verthda965502017-04-11 15:29:14 -0400100
Jim Van Verthbdde4282018-06-14 09:09:18 -0400101 compute_offset(d0, p0, side, outerTangentIntersect, vector0);
102 compute_offset(d1, p1, side, outerTangentIntersect, vector1);
Jim Van Verthda965502017-04-11 15:29:14 -0400103 }
104
105 return true;
Brian Salomonab664fa2017-03-24 16:07:20 +0000106}
107
Jim Van Verthbdde4282018-06-14 09:09:18 -0400108// Offset line segment p0-p1 'd0' and 'd1' units in the direction specified by 'side'
109bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
110 int side, SkPoint* offset0, SkPoint* offset1) {
111 SkVector v0, v1;
112 if (!compute_offset_vectors(p0, p1, d0, d1, side, &v0, &v1)) {
113 return false;
114 }
115 *offset0 = p0 + v0;
116 *offset1 = p1 + v1;
117
118 return true;
119}
120
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400121// compute fraction of d along v
122static inline SkScalar compute_param(const SkVector& v, const SkVector& d) {
123 if (SkScalarNearlyZero(v.fX)) {
124 return d.fY / v.fY;
125 } else {
126 return d.fX / v.fX;
127 }
128}
129
Brian Salomonab664fa2017-03-24 16:07:20 +0000130// Compute the intersection 'p' between segments s0 and s1, if any.
131// 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'.
132// Returns false if there is no intersection.
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400133static bool compute_intersection(const OffsetSegment& s0, const OffsetSegment& s1,
Brian Salomonab664fa2017-03-24 16:07:20 +0000134 SkPoint* p, SkScalar* s, SkScalar* t) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400135 // Common cases for polygon chains -- check if endpoints are touching
136 if (SkPointPriv::EqualsWithinTolerance(s0.fP1, s1.fP0)) {
137 *p = s0.fP1;
138 *s = SK_Scalar1;
139 *t = 0;
140 return true;
141 }
142 if (SkPointPriv::EqualsWithinTolerance(s1.fP1, s0.fP0)) {
143 *p = s1.fP1;
144 *s = 0;
145 *t = SK_Scalar1;
146 return true;
147 }
148
Brian Salomonab664fa2017-03-24 16:07:20 +0000149 SkVector v0 = s0.fP1 - s0.fP0;
150 SkVector v1 = s1.fP1 - s1.fP0;
Brian Salomonab664fa2017-03-24 16:07:20 +0000151 SkVector d = s1.fP0 - s0.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400152 SkScalar perpDot = v0.cross(v1);
153 SkScalar localS, localT;
154 if (SkScalarNearlyZero(perpDot)) {
155 // segments are parallel, but not collinear
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400156 if (!SkScalarNearlyZero(d.cross(v0)) || !SkScalarNearlyZero(d.cross(v1))) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400157 return false;
158 }
159
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400160 // Check for degenerate segments
161 if (!SkPointPriv::CanNormalize(v0.fX, v0.fY)) {
162 // Both are degenerate
163 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
164 // Check if they're the same point
165 if (!SkPointPriv::CanNormalize(d.fX, d.fY)) {
166 *p = s0.fP0;
167 *s = 0;
168 *t = 0;
169 return true;
170 } else {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400171 return false;
172 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400173 }
174 // Otherwise project onto segment1
175 localT = compute_param(v1, -d);
176 if (localT < 0 || localT > SK_Scalar1) {
177 return false;
178 }
179 localS = 0;
180 } else {
181 // Project segment1's endpoints onto segment0
182 localS = compute_param(v0, d);
183 localT = 0;
184 if (localS < 0 || localS > SK_Scalar1) {
185 // The first endpoint doesn't lie on segment0
186 // If segment1 is degenerate, then there's no collision
187 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
188 return false;
189 }
190
191 // Otherwise try the other one
192 SkScalar oldLocalS = localS;
193 localS = compute_param(v0, s1.fP1 - s0.fP0);
194 localT = SK_Scalar1;
195 if (localS < 0 || localS > SK_Scalar1) {
196 // it's possible that segment1's interval surrounds segment0
197 // this is false if params have the same signs, and in that case no collision
198 if (localS*oldLocalS > 0) {
199 return false;
200 }
201 // otherwise project segment0's endpoint onto segment1 instead
202 localS = 0;
203 localT = compute_param(v1, -d);
204 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400205 }
206 }
207 } else {
208 localS = d.cross(v1) / perpDot;
209 if (localS < 0 || localS > SK_Scalar1) {
210 return false;
211 }
212 localT = d.cross(v0) / perpDot;
213 if (localT < 0 || localT > SK_Scalar1) {
214 return false;
215 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000216 }
217
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400218 *p = s0.fP0 + v0*localS;
Brian Salomonab664fa2017-03-24 16:07:20 +0000219 *s = localS;
220 *t = localT;
221
222 return true;
223}
224
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400225// computes the line intersection and then the distance to s0's endpoint
226static SkScalar compute_crossing_distance(const OffsetSegment& s0, const OffsetSegment& s1) {
227 SkVector v0 = s0.fP1 - s0.fP0;
228 SkVector v1 = s1.fP1 - s1.fP0;
229
230 SkScalar perpDot = v0.cross(v1);
231 if (SkScalarNearlyZero(perpDot)) {
232 // segments are parallel
233 return SK_ScalarMax;
234 }
235
236 SkVector d = s1.fP0 - s0.fP0;
237 SkScalar localS = d.cross(v1) / perpDot;
238 if (localS < 0) {
239 localS = -localS;
240 } else {
241 localS -= SK_Scalar1;
242 }
243
244 localS *= v0.length();
245
246 return localS;
247}
248
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400249bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
250 if (polygonSize < 3) {
251 return false;
Jim Van Verth0513f142017-03-24 14:28:57 -0400252 }
253
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400254 SkScalar lastArea = 0;
255 SkScalar lastPerpDot = 0;
Jim Van Verth0513f142017-03-24 14:28:57 -0400256
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400257 int prevIndex = polygonSize - 1;
258 int currIndex = 0;
259 int nextIndex = 1;
260 SkPoint origin = polygonVerts[0];
261 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
262 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
263 SkVector w0 = polygonVerts[currIndex] - origin;
264 SkVector w1 = polygonVerts[nextIndex] - origin;
265 for (int i = 0; i < polygonSize; ++i) {
266 // Check that winding direction is always the same (otherwise we have a reflex vertex)
Jim Van Verth0513f142017-03-24 14:28:57 -0400267 SkScalar perpDot = v0.cross(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400268 if (lastPerpDot*perpDot < 0) {
Jim Van Verth0513f142017-03-24 14:28:57 -0400269 return false;
270 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400271 if (0 != perpDot) {
272 lastPerpDot = perpDot;
273 }
274
275 // If the signed area ever flips it's concave
276 // TODO: see if we can verify convexity only with signed area
277 SkScalar quadArea = w0.cross(w1);
278 if (quadArea*lastArea < 0) {
279 return false;
280 }
281 if (0 != quadArea) {
282 lastArea = quadArea;
283 }
284
285 prevIndex = currIndex;
286 currIndex = nextIndex;
287 nextIndex = (currIndex + 1) % polygonSize;
288 v0 = v1;
289 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
290 w0 = w1;
291 w1 = polygonVerts[nextIndex] - origin;
Jim Van Verth0513f142017-03-24 14:28:57 -0400292 }
293
294 return true;
295}
Jim Van Verth0513f142017-03-24 14:28:57 -0400296
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400297struct EdgeData {
298 OffsetSegment fInset;
299 SkPoint fIntersection;
300 SkScalar fTValue;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400301 uint16_t fStart;
302 uint16_t fEnd;
303 uint16_t fIndex;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400304 bool fValid;
305
306 void init() {
307 fIntersection = fInset.fP0;
308 fTValue = SK_ScalarMin;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400309 fStart = 0;
310 fEnd = 0;
311 fIndex = 0;
312 fValid = true;
313 }
314
315 void init(uint16_t start, uint16_t end) {
316 fIntersection = fInset.fP0;
317 fTValue = SK_ScalarMin;
318 fStart = start;
319 fEnd = end;
320 fIndex = start;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400321 fValid = true;
322 }
323};
324
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400325//////////////////////////////////////////////////////////////////////////////////
326
Brian Salomonab664fa2017-03-24 16:07:20 +0000327// The objective here is to inset all of the edges by the given distance, and then
328// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
329// we should only be making left-hand turns (for cw polygons, we use the winding
330// parameter to reverse this). We detect this by checking whether the second intersection
331// on an edge is closer to its tail than the first one.
332//
333// We might also have the case that there is no intersection between two neighboring inset edges.
334// In this case, one edge will lie to the right of the other and should be discarded along with
335// its previous intersection (if any).
336//
337// Note: the assumption is that inputPolygon is convex and has no coincident points.
338//
339bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400340 std::function<SkScalar(const SkPoint&)> insetDistanceFunc,
Jim Van Verthda965502017-04-11 15:29:14 -0400341 SkTDArray<SkPoint>* insetPolygon) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000342 if (inputPolygonSize < 3) {
343 return false;
344 }
345
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400346 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400347 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Brian Salomonab664fa2017-03-24 16:07:20 +0000348 if (0 == winding) {
349 return false;
350 }
351
352 // set up
Brian Salomonab664fa2017-03-24 16:07:20 +0000353 SkAutoSTMalloc<64, EdgeData> edgeData(inputPolygonSize);
354 for (int i = 0; i < inputPolygonSize; ++i) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000355 int j = (i + 1) % inputPolygonSize;
Jim Van Verthb55eb282017-07-18 14:13:45 -0400356 int k = (i + 2) % inputPolygonSize;
357 // check for convexity just to be sure
358 if (compute_side(inputPolygonVerts[i], inputPolygonVerts[j],
359 inputPolygonVerts[k])*winding < 0) {
360 return false;
361 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400362 if (!SkOffsetSegment(inputPolygonVerts[i], inputPolygonVerts[j],
Jim Van Verthbdde4282018-06-14 09:09:18 -0400363 insetDistanceFunc(inputPolygonVerts[i]),
364 insetDistanceFunc(inputPolygonVerts[j]),
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400365 winding,
366 &edgeData[i].fInset.fP0, &edgeData[i].fInset.fP1)) {
367 return false;
368 }
369 edgeData[i].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000370 }
371
372 int prevIndex = inputPolygonSize - 1;
373 int currIndex = 0;
374 int insetVertexCount = inputPolygonSize;
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400375 int iterations = 0;
Brian Salomonab664fa2017-03-24 16:07:20 +0000376 while (prevIndex != currIndex) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400377 ++iterations;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400378 // we should check each edge against each other edge at most once
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400379 if (iterations > inputPolygonSize*inputPolygonSize) {
380 return false;
381 }
382
Brian Salomonab664fa2017-03-24 16:07:20 +0000383 if (!edgeData[prevIndex].fValid) {
384 prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize;
385 continue;
386 }
387
388 SkScalar s, t;
389 SkPoint intersection;
390 if (compute_intersection(edgeData[prevIndex].fInset, edgeData[currIndex].fInset,
391 &intersection, &s, &t)) {
392 // if new intersection is further back on previous inset from the prior intersection
393 if (s < edgeData[prevIndex].fTValue) {
394 // no point in considering this one again
395 edgeData[prevIndex].fValid = false;
396 --insetVertexCount;
397 // go back one segment
398 prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize;
399 // we've already considered this intersection, we're done
400 } else if (edgeData[currIndex].fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500401 SkPointPriv::EqualsWithinTolerance(intersection,
402 edgeData[currIndex].fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000403 1.0e-6f)) {
404 break;
405 } else {
406 // add intersection
407 edgeData[currIndex].fIntersection = intersection;
408 edgeData[currIndex].fTValue = t;
409
410 // go to next segment
411 prevIndex = currIndex;
412 currIndex = (currIndex + 1) % inputPolygonSize;
413 }
414 } else {
415 // if prev to right side of curr
416 int side = winding*compute_side(edgeData[currIndex].fInset.fP0,
417 edgeData[currIndex].fInset.fP1,
418 edgeData[prevIndex].fInset.fP1);
419 if (side < 0 && side == winding*compute_side(edgeData[currIndex].fInset.fP0,
420 edgeData[currIndex].fInset.fP1,
421 edgeData[prevIndex].fInset.fP0)) {
422 // no point in considering this one again
423 edgeData[prevIndex].fValid = false;
424 --insetVertexCount;
425 // go back one segment
426 prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize;
427 } else {
428 // move to next segment
429 edgeData[currIndex].fValid = false;
430 --insetVertexCount;
431 currIndex = (currIndex + 1) % inputPolygonSize;
432 }
433 }
434 }
435
Jim Van Verthda965502017-04-11 15:29:14 -0400436 // store all the valid intersections that aren't nearly coincident
437 // TODO: look at the main algorithm and see if we can detect these better
438 static constexpr SkScalar kCleanupTolerance = 0.01f;
439
Brian Salomonab664fa2017-03-24 16:07:20 +0000440 insetPolygon->reset();
Mike Klein23e45442018-04-19 09:29:02 -0400441 if (insetVertexCount >= 0) {
442 insetPolygon->setReserve(insetVertexCount);
443 }
Jim Van Verthda965502017-04-11 15:29:14 -0400444 currIndex = -1;
Brian Salomonab664fa2017-03-24 16:07:20 +0000445 for (int i = 0; i < inputPolygonSize; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400446 if (edgeData[i].fValid && (currIndex == -1 ||
Cary Clarkdf429f32017-11-08 11:44:31 -0500447 !SkPointPriv::EqualsWithinTolerance(edgeData[i].fIntersection,
448 (*insetPolygon)[currIndex],
449 kCleanupTolerance))) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000450 *insetPolygon->push() = edgeData[i].fIntersection;
Jim Van Verthda965502017-04-11 15:29:14 -0400451 currIndex++;
Brian Salomonab664fa2017-03-24 16:07:20 +0000452 }
453 }
Jim Van Verthda965502017-04-11 15:29:14 -0400454 // make sure the first and last points aren't coincident
455 if (currIndex >= 1 &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500456 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
457 kCleanupTolerance)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400458 insetPolygon->pop();
459 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000460
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400461 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000462}
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400463
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400464///////////////////////////////////////////////////////////////////////////////////////////
465
466// compute the number of points needed for a circular join when offsetting a reflex vertex
467void SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar r,
468 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400469 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
470
471 SkScalar rCos = v1.dot(v2);
472 SkScalar rSin = v1.cross(v2);
473 SkScalar theta = SkScalarATan2(rSin, rCos);
474
475 int steps = SkScalarRoundToInt(SkScalarAbs(r*theta*kRecipPixelsPerArcSegment));
476
477 SkScalar dTheta = theta / steps;
478 *rotSin = SkScalarSinCos(dTheta, rotCos);
479 *n = steps;
480}
481
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400482///////////////////////////////////////////////////////////////////////////////////////////
483
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400484// tolerant less-than comparison
485static inline bool nearly_lt(SkScalar a, SkScalar b, SkScalar tolerance = SK_ScalarNearlyZero) {
486 return a < b - tolerance;
487}
488
489// a point is "left" to another if its x coordinate is less, or if equal, its y coordinate
490static bool left(const SkPoint& p0, const SkPoint& p1) {
491 return nearly_lt(p0.fX, p1.fX) ||
492 (SkScalarNearlyEqual(p0.fX, p1.fX) && nearly_lt(p0.fY, p1.fY));
493}
494
495struct Vertex {
496 static bool Left(const Vertex& qv0, const Vertex& qv1) {
497 return left(qv0.fPosition, qv1.fPosition);
498 }
499 // packed to fit into 16 bytes (one cache line)
500 SkPoint fPosition;
501 uint16_t fIndex; // index in unsorted polygon
502 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
503 uint16_t fNextIndex;
504 uint16_t fFlags;
505};
506
507enum VertexFlags {
508 kPrevLeft_VertexFlag = 0x1,
509 kNextLeft_VertexFlag = 0x2,
510};
511
512struct Edge {
513 // returns true if "this" is above "that"
514 bool above(const Edge& that, SkScalar tolerance = SK_ScalarNearlyZero) {
515 SkASSERT(nearly_lt(this->fSegment.fP0.fX, that.fSegment.fP0.fX, tolerance) ||
516 SkScalarNearlyEqual(this->fSegment.fP0.fX, that.fSegment.fP0.fX, tolerance));
517 // The idea here is that if the vector between the origins of the two segments (dv)
518 // rotates counterclockwise up to the vector representing the "this" segment (u),
519 // then we know that "this" is above that. If the result is clockwise we say it's below.
520 SkVector dv = that.fSegment.fP0 - this->fSegment.fP0;
521 SkVector u = this->fSegment.fP1 - this->fSegment.fP0;
522 SkScalar cross = dv.cross(u);
523 if (cross > tolerance) {
524 return true;
525 } else if (cross < -tolerance) {
526 return false;
527 }
528 // If the result is 0 then either the two origins are equal or the origin of "that"
529 // lies on dv. So then we try the same for the vector from the tail of "this"
530 // to the head of "that". Again, ccw means "this" is above "that".
531 dv = that.fSegment.fP1 - this->fSegment.fP0;
532 return (dv.cross(u) > tolerance);
533 }
534
535 bool intersect(const Edge& that) const {
536 SkPoint intersection;
537 SkScalar s, t;
538 // check first to see if these edges are neighbors in the polygon
539 if (this->fIndex0 == that.fIndex0 || this->fIndex1 == that.fIndex0 ||
540 this->fIndex0 == that.fIndex1 || this->fIndex1 == that.fIndex1) {
541 return false;
542 }
543 return compute_intersection(this->fSegment, that.fSegment, &intersection, &s, &t);
544 }
545
546 bool operator==(const Edge& that) const {
547 return (this->fIndex0 == that.fIndex0 && this->fIndex1 == that.fIndex1);
548 }
549
550 bool operator!=(const Edge& that) const {
551 return !operator==(that);
552 }
553
554 OffsetSegment fSegment;
555 int32_t fIndex0; // indices for previous and next vertex
556 int32_t fIndex1;
557};
558
559class EdgeList {
560public:
561 void reserve(int count) { fEdges.reserve(count); }
562
563 bool insert(const Edge& newEdge) {
564 // linear search for now (expected case is very few active edges)
565 int insertIndex = 0;
566 while (insertIndex < fEdges.count() && fEdges[insertIndex].above(newEdge)) {
567 ++insertIndex;
568 }
569 // if we intersect with the existing edge above or below us
570 // then we know this polygon is not simple, so don't insert, just fail
571 if (insertIndex > 0 && newEdge.intersect(fEdges[insertIndex - 1])) {
572 return false;
573 }
574 if (insertIndex < fEdges.count() && newEdge.intersect(fEdges[insertIndex])) {
575 return false;
576 }
577
578 fEdges.push_back();
579 for (int i = fEdges.count() - 1; i > insertIndex; --i) {
580 fEdges[i] = fEdges[i - 1];
581 }
582 fEdges[insertIndex] = newEdge;
583
584 return true;
585 }
586
587 bool remove(const Edge& edge) {
588 SkASSERT(fEdges.count() > 0);
589
590 // linear search for now (expected case is very few active edges)
591 int removeIndex = 0;
592 while (removeIndex < fEdges.count() && fEdges[removeIndex] != edge) {
593 ++removeIndex;
594 }
595 // we'd better find it or something is wrong
596 SkASSERT(removeIndex < fEdges.count());
597
598 // if we intersect with the edge above or below us
599 // then we know this polygon is not simple, so don't remove, just fail
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400600 if (removeIndex > 0 && fEdges[removeIndex].intersect(fEdges[removeIndex - 1])) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400601 return false;
602 }
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400603 if (removeIndex < fEdges.count() - 1) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400604 if (fEdges[removeIndex].intersect(fEdges[removeIndex + 1])) {
605 return false;
606 }
607 // copy over the old entry
608 memmove(&fEdges[removeIndex], &fEdges[removeIndex + 1],
609 sizeof(Edge)*(fEdges.count() - removeIndex - 1));
610 }
611
612 fEdges.pop_back();
613 return true;
614 }
615
616private:
617 SkSTArray<1, Edge> fEdges;
618};
619
620// Here we implement a sweep line algorithm to determine whether the provided points
621// represent a simple polygon, i.e., the polygon is non-self-intersecting.
622// We first insert the vertices into a priority queue sorting horizontally from left to right.
623// Then as we pop the vertices from the queue we generate events which indicate that an edge
624// should be added or removed from an edge list. If any intersections are detected in the edge
625// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400626bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400627 SkTDPQueue <Vertex, Vertex::Left> vertexQueue;
628 EdgeList sweepLine;
629
630 sweepLine.reserve(polygonSize);
631 for (int i = 0; i < polygonSize; ++i) {
632 Vertex newVertex;
633 newVertex.fPosition = polygon[i];
634 newVertex.fIndex = i;
635 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
636 newVertex.fNextIndex = (i + 1) % polygonSize;
637 newVertex.fFlags = 0;
638 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
639 newVertex.fFlags |= kPrevLeft_VertexFlag;
640 }
641 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
642 newVertex.fFlags |= kNextLeft_VertexFlag;
643 }
644 vertexQueue.insert(newVertex);
645 }
646
647 // pop each vertex from the queue and generate events depending on
648 // where it lies relative to its neighboring edges
649 while (vertexQueue.count() > 0) {
650 const Vertex& v = vertexQueue.peek();
651
652 // check edge to previous vertex
653 if (v.fFlags & kPrevLeft_VertexFlag) {
654 Edge edge{ { polygon[v.fPrevIndex], v.fPosition }, v.fPrevIndex, v.fIndex };
655 if (!sweepLine.remove(edge)) {
656 break;
657 }
658 } else {
659 Edge edge{ { v.fPosition, polygon[v.fPrevIndex] }, v.fIndex, v.fPrevIndex };
660 if (!sweepLine.insert(edge)) {
661 break;
662 }
663 }
664
665 // check edge to next vertex
666 if (v.fFlags & kNextLeft_VertexFlag) {
667 Edge edge{ { polygon[v.fNextIndex], v.fPosition }, v.fNextIndex, v.fIndex };
668 if (!sweepLine.remove(edge)) {
669 break;
670 }
671 } else {
672 Edge edge{ { v.fPosition, polygon[v.fNextIndex] }, v.fIndex, v.fNextIndex };
673 if (!sweepLine.insert(edge)) {
674 break;
675 }
676 }
677
678 vertexQueue.pop();
679 }
680
681 return (vertexQueue.count() == 0);
682}
683
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400684///////////////////////////////////////////////////////////////////////////////////////////
685
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400686bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400687 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
688 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400689 if (inputPolygonSize < 3) {
690 return false;
691 }
692
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400693 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400694 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400695 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400696 return false;
697 }
698
Jim Van Verthbdde4282018-06-14 09:09:18 -0400699 // build normals
700 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
701 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
702 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400703 for (int curr = 0; curr < inputPolygonSize; ++curr) {
Jim Van Verthbdde4282018-06-14 09:09:18 -0400704 int next = (curr + 1) % inputPolygonSize;
705 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[next]);
706 if (!compute_offset_vectors(inputPolygonVerts[curr], inputPolygonVerts[next],
707 currOffset, nextOffset, winding,
708 &normal0[curr], &normal1[next])) {
709 return false;
710 }
711 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400712 }
713
714 // build initial offset edge list
715 SkSTArray<64, EdgeData> edgeData(inputPolygonSize);
716 int prevIndex = inputPolygonSize - 1;
717 int currIndex = 0;
718 int nextIndex = 1;
719 while (currIndex < inputPolygonSize) {
720 int side = compute_side(inputPolygonVerts[prevIndex],
721 inputPolygonVerts[currIndex],
722 inputPolygonVerts[nextIndex]);
Jim Van Verthbdde4282018-06-14 09:09:18 -0400723 SkScalar offset = offsetDistanceFunc(inputPolygonVerts[currIndex]);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400724 // if reflex point, fill in curve
725 if (side*winding*offset < 0) {
726 SkScalar rotSin, rotCos;
727 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400728 SkVector prevNormal = normal1[currIndex];
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400729 SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400730 &rotSin, &rotCos, &numSteps);
731 for (int i = 0; i < numSteps - 1; ++i) {
732 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
733 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
734 EdgeData& edge = edgeData.push_back();
735 edge.fInset.fP0 = inputPolygonVerts[currIndex] + prevNormal;
736 edge.fInset.fP1 = inputPolygonVerts[currIndex] + currNormal;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400737 edge.init(currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400738 prevNormal = currNormal;
739 }
740 EdgeData& edge = edgeData.push_back();
741 edge.fInset.fP0 = inputPolygonVerts[currIndex] + prevNormal;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400742 edge.fInset.fP1 = inputPolygonVerts[currIndex] + normal0[currIndex];
Jim Van Verth872da6b2018-04-10 11:24:11 -0400743 edge.init(currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400744 }
745
746 // Add the edge
747 EdgeData& edge = edgeData.push_back();
Jim Van Verthbdde4282018-06-14 09:09:18 -0400748 edge.fInset.fP0 = inputPolygonVerts[currIndex] + normal0[currIndex];
749 edge.fInset.fP1 = inputPolygonVerts[nextIndex] + normal1[nextIndex];
Jim Van Verth872da6b2018-04-10 11:24:11 -0400750 edge.init(currIndex, nextIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400751
752 prevIndex = currIndex;
753 currIndex++;
754 nextIndex = (nextIndex + 1) % inputPolygonSize;
755 }
756
757 int edgeDataSize = edgeData.count();
758 prevIndex = edgeDataSize - 1;
759 currIndex = 0;
760 int insetVertexCount = edgeDataSize;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400761 int iterations = 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400762 while (prevIndex != currIndex) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400763 ++iterations;
764 // we should check each edge against each other edge at most once
765 if (iterations > edgeDataSize*edgeDataSize) {
766 return false;
767 }
768
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400769 if (!edgeData[prevIndex].fValid) {
770 prevIndex = (prevIndex + edgeDataSize - 1) % edgeDataSize;
771 continue;
772 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400773 if (!edgeData[currIndex].fValid) {
774 currIndex = (currIndex + 1) % edgeDataSize;
775 continue;
776 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400777
778 SkScalar s, t;
779 SkPoint intersection;
780 if (compute_intersection(edgeData[prevIndex].fInset, edgeData[currIndex].fInset,
781 &intersection, &s, &t)) {
782 // if new intersection is further back on previous inset from the prior intersection
783 if (s < edgeData[prevIndex].fTValue) {
784 // no point in considering this one again
785 edgeData[prevIndex].fValid = false;
786 --insetVertexCount;
787 // go back one segment
788 prevIndex = (prevIndex + edgeDataSize - 1) % edgeDataSize;
789 // we've already considered this intersection, we're done
790 } else if (edgeData[currIndex].fTValue > SK_ScalarMin &&
791 SkPointPriv::EqualsWithinTolerance(intersection,
792 edgeData[currIndex].fIntersection,
793 1.0e-6f)) {
794 break;
795 } else {
796 // add intersection
797 edgeData[currIndex].fIntersection = intersection;
798 edgeData[currIndex].fTValue = t;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400799 edgeData[currIndex].fIndex = edgeData[prevIndex].fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400800
801 // go to next segment
802 prevIndex = currIndex;
803 currIndex = (currIndex + 1) % edgeDataSize;
804 }
805 } else {
806 // If there is no intersection, we want to minimize the distance between
807 // the point where the segment lines cross and the segments themselves.
808 SkScalar prevPrevIndex = (prevIndex + edgeDataSize - 1) % edgeDataSize;
809 SkScalar currNextIndex = (currIndex + 1) % edgeDataSize;
810 SkScalar dist0 = compute_crossing_distance(edgeData[currIndex].fInset,
811 edgeData[prevPrevIndex].fInset);
812 SkScalar dist1 = compute_crossing_distance(edgeData[prevIndex].fInset,
813 edgeData[currNextIndex].fInset);
814 if (dist0 < dist1) {
815 edgeData[prevIndex].fValid = false;
816 prevIndex = prevPrevIndex;
817 } else {
818 edgeData[currIndex].fValid = false;
819 currIndex = currNextIndex;
820 }
821 --insetVertexCount;
822 }
823 }
824
825 // store all the valid intersections that aren't nearly coincident
826 // TODO: look at the main algorithm and see if we can detect these better
827 static constexpr SkScalar kCleanupTolerance = 0.01f;
828
829 offsetPolygon->reset();
830 offsetPolygon->setReserve(insetVertexCount);
831 currIndex = -1;
832 for (int i = 0; i < edgeData.count(); ++i) {
833 if (edgeData[i].fValid && (currIndex == -1 ||
834 !SkPointPriv::EqualsWithinTolerance(edgeData[i].fIntersection,
835 (*offsetPolygon)[currIndex],
836 kCleanupTolerance))) {
837 *offsetPolygon->push() = edgeData[i].fIntersection;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400838 if (polygonIndices) {
839 *polygonIndices->push() = edgeData[i].fIndex;
840 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400841 currIndex++;
842 }
843 }
844 // make sure the first and last points aren't coincident
845 if (currIndex >= 1 &&
846 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
847 kCleanupTolerance)) {
848 offsetPolygon->pop();
Jim Van Verth872da6b2018-04-10 11:24:11 -0400849 if (polygonIndices) {
850 polygonIndices->pop();
851 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400852 }
853
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400854 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400855 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400856
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400857 return (winding*offsetWinding > 0 &&
858 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400859}
860
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400861//////////////////////////////////////////////////////////////////////////////////////////
862
863struct TriangulationVertex {
864 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
865
866 enum class VertexType { kConvex, kReflex };
867
868 SkPoint fPosition;
869 VertexType fVertexType;
870 uint16_t fIndex;
871 uint16_t fPrevIndex;
872 uint16_t fNextIndex;
873};
874
875// test to see if point p is in triangle p0p1p2.
876// for now assuming strictly inside -- if on the edge it's outside
877static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
878 const SkPoint& p) {
879 SkVector v0 = p1 - p0;
880 SkVector v1 = p2 - p1;
881 SkScalar n = v0.cross(v1);
882
883 SkVector w0 = p - p0;
884 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
885 return false;
886 }
887
888 SkVector w1 = p - p1;
889 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
890 return false;
891 }
892
893 SkVector v2 = p0 - p2;
894 SkVector w2 = p - p2;
895 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
896 return false;
897 }
898
899 return true;
900}
901
902// Data structure to track reflex vertices and check whether any are inside a given triangle
903class ReflexHash {
904public:
905 void add(TriangulationVertex* v) {
906 fReflexList.addToTail(v);
907 }
908
909 void remove(TriangulationVertex* v) {
910 fReflexList.remove(v);
911 }
912
913 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
914 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
915 reflexIter != fReflexList.end(); ++reflexIter) {
916 TriangulationVertex* reflexVertex = *reflexIter;
917 if (point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
918 return true;
919 }
920 }
921
922 return false;
923 }
924
925private:
926 // TODO: switch to an actual spatial hash
927 SkTInternalLList<TriangulationVertex> fReflexList;
928};
929
930// Check to see if a reflex vertex has become a convex vertex after clipping an ear
931static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
932 int winding, ReflexHash* reflexHash,
933 SkTInternalLList<TriangulationVertex>* convexList) {
934 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
935 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
936 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
937 if (winding*v0.cross(v1) > SK_ScalarNearlyZero) {
938 p->fVertexType = TriangulationVertex::VertexType::kConvex;
939 reflexHash->remove(p);
940 p->fPrev = p->fNext = nullptr;
941 convexList->addToTail(p);
942 }
943 }
944}
945
946bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
947 SkTDArray<uint16_t>* triangleIndices) {
948 if (polygonSize < 3) {
949 return false;
950 }
951 // need to be able to represent all the vertices in the 16-bit indices
952 if (polygonSize >= (1 << 16)) {
953 return false;
954 }
955
956 // get winding direction
957 // TODO: we do this for all the polygon routines -- might be better to have the client
958 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400959 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400960 if (0 == winding) {
961 return false;
962 }
963
964 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
965 // TODO: possibly sort the convexList in some way to get better triangles
966 SkTInternalLList<TriangulationVertex> convexList;
967 ReflexHash reflexHash;
968 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
969 int prevIndex = polygonSize - 1;
970 int currIndex = 0;
971 int nextIndex = 1;
972 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
973 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
974 for (int i = 0; i < polygonSize; ++i) {
975 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
976 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
977 triangulationVertices[currIndex].fIndex = currIndex;
978 triangulationVertices[currIndex].fPrevIndex = prevIndex;
979 triangulationVertices[currIndex].fNextIndex = nextIndex;
980 if (winding*v0.cross(v1) > SK_ScalarNearlyZero) {
981 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
982 convexList.addToTail(&triangulationVertices[currIndex]);
983 } else {
984 // We treat near collinear vertices as reflex
985 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
986 reflexHash.add(&triangulationVertices[currIndex]);
987 }
988
989 prevIndex = currIndex;
990 currIndex = nextIndex;
991 nextIndex = (currIndex + 1) % polygonSize;
992 v0 = v1;
993 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
994 }
995
996 // The general concept: We are trying to find three neighboring vertices where
997 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
998 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
999 // we have triangulated the entire polygon.
1000 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1001 // noting that only convex vertices can be potential ears, and we only need to check whether
1002 // any reflex vertices lie inside the ear.
1003 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1004 int vertexCount = polygonSize;
1005 while (vertexCount > 3) {
1006 bool success = false;
1007 TriangulationVertex* earVertex = nullptr;
1008 TriangulationVertex* p0 = nullptr;
1009 TriangulationVertex* p2 = nullptr;
1010 // find a convex vertex to clip
1011 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1012 convexIter != convexList.end(); ++convexIter) {
1013 earVertex = *convexIter;
1014 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1015
1016 p0 = &triangulationVertices[earVertex->fPrevIndex];
1017 p2 = &triangulationVertices[earVertex->fNextIndex];
1018
1019 // see if any reflex vertices are inside the ear
1020 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
1021 p2->fPosition);
1022 if (failed) {
1023 continue;
1024 }
1025
1026 // found one we can clip
1027 success = true;
1028 break;
1029 }
1030 // If we can't find any ears to clip, this probably isn't a simple polygon
1031 if (!success) {
1032 return false;
1033 }
1034
1035 // add indices
1036 auto indices = triangleIndices->append(3);
1037 indices[0] = indexMap[p0->fIndex];
1038 indices[1] = indexMap[earVertex->fIndex];
1039 indices[2] = indexMap[p2->fIndex];
1040
1041 // clip the ear
1042 convexList.remove(earVertex);
1043 --vertexCount;
1044
1045 // reclassify reflex verts
1046 p0->fNextIndex = earVertex->fNextIndex;
1047 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1048
1049 p2->fPrevIndex = earVertex->fPrevIndex;
1050 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1051 }
1052
1053 // output indices
1054 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1055 vertexIter != convexList.end(); ++vertexIter) {
1056 TriangulationVertex* vertex = *vertexIter;
1057 *triangleIndices->push() = indexMap[vertex->fIndex];
1058 }
1059
1060 return true;
1061}