blob: b76d270d15d70e0dacf0ee74d4f2db4ae927b395 [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) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400266 if (!polygonVerts[i].isFinite()) {
267 return false;
268 }
269
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400270 // Check that winding direction is always the same (otherwise we have a reflex vertex)
Jim Van Verth0513f142017-03-24 14:28:57 -0400271 SkScalar perpDot = v0.cross(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400272 if (lastPerpDot*perpDot < 0) {
Jim Van Verth0513f142017-03-24 14:28:57 -0400273 return false;
274 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400275 if (0 != perpDot) {
276 lastPerpDot = perpDot;
277 }
278
279 // If the signed area ever flips it's concave
280 // TODO: see if we can verify convexity only with signed area
281 SkScalar quadArea = w0.cross(w1);
282 if (quadArea*lastArea < 0) {
283 return false;
284 }
285 if (0 != quadArea) {
286 lastArea = quadArea;
287 }
288
289 prevIndex = currIndex;
290 currIndex = nextIndex;
291 nextIndex = (currIndex + 1) % polygonSize;
292 v0 = v1;
293 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
294 w0 = w1;
295 w1 = polygonVerts[nextIndex] - origin;
Jim Van Verth0513f142017-03-24 14:28:57 -0400296 }
297
298 return true;
299}
Jim Van Verth0513f142017-03-24 14:28:57 -0400300
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400301struct EdgeData {
302 OffsetSegment fInset;
303 SkPoint fIntersection;
304 SkScalar fTValue;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400305 uint16_t fStart;
306 uint16_t fEnd;
307 uint16_t fIndex;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400308 bool fValid;
309
310 void init() {
311 fIntersection = fInset.fP0;
312 fTValue = SK_ScalarMin;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400313 fStart = 0;
314 fEnd = 0;
315 fIndex = 0;
316 fValid = true;
317 }
318
319 void init(uint16_t start, uint16_t end) {
320 fIntersection = fInset.fP0;
321 fTValue = SK_ScalarMin;
322 fStart = start;
323 fEnd = end;
324 fIndex = start;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400325 fValid = true;
326 }
327};
328
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400329//////////////////////////////////////////////////////////////////////////////////
330
Brian Salomonab664fa2017-03-24 16:07:20 +0000331// The objective here is to inset all of the edges by the given distance, and then
332// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
333// we should only be making left-hand turns (for cw polygons, we use the winding
334// parameter to reverse this). We detect this by checking whether the second intersection
335// on an edge is closer to its tail than the first one.
336//
337// We might also have the case that there is no intersection between two neighboring inset edges.
338// In this case, one edge will lie to the right of the other and should be discarded along with
339// its previous intersection (if any).
340//
341// Note: the assumption is that inputPolygon is convex and has no coincident points.
342//
343bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400344 std::function<SkScalar(const SkPoint&)> insetDistanceFunc,
Jim Van Verthda965502017-04-11 15:29:14 -0400345 SkTDArray<SkPoint>* insetPolygon) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000346 if (inputPolygonSize < 3) {
347 return false;
348 }
349
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400350 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400351 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Brian Salomonab664fa2017-03-24 16:07:20 +0000352 if (0 == winding) {
353 return false;
354 }
355
356 // set up
Brian Salomonab664fa2017-03-24 16:07:20 +0000357 SkAutoSTMalloc<64, EdgeData> edgeData(inputPolygonSize);
358 for (int i = 0; i < inputPolygonSize; ++i) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000359 int j = (i + 1) % inputPolygonSize;
Jim Van Verthb55eb282017-07-18 14:13:45 -0400360 int k = (i + 2) % inputPolygonSize;
Jim Van Verth061cc212018-07-11 14:09:09 -0400361 if (!inputPolygonVerts[i].isFinite()) {
362 return false;
363 }
Jim Van Verthb55eb282017-07-18 14:13:45 -0400364 // check for convexity just to be sure
365 if (compute_side(inputPolygonVerts[i], inputPolygonVerts[j],
366 inputPolygonVerts[k])*winding < 0) {
367 return false;
368 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400369 if (!SkOffsetSegment(inputPolygonVerts[i], inputPolygonVerts[j],
Jim Van Verthbdde4282018-06-14 09:09:18 -0400370 insetDistanceFunc(inputPolygonVerts[i]),
371 insetDistanceFunc(inputPolygonVerts[j]),
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400372 winding,
373 &edgeData[i].fInset.fP0, &edgeData[i].fInset.fP1)) {
374 return false;
375 }
376 edgeData[i].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000377 }
378
379 int prevIndex = inputPolygonSize - 1;
380 int currIndex = 0;
381 int insetVertexCount = inputPolygonSize;
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400382 int iterations = 0;
Brian Salomonab664fa2017-03-24 16:07:20 +0000383 while (prevIndex != currIndex) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400384 ++iterations;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400385 // we should check each edge against each other edge at most once
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400386 if (iterations > inputPolygonSize*inputPolygonSize) {
387 return false;
388 }
389
Brian Salomonab664fa2017-03-24 16:07:20 +0000390 if (!edgeData[prevIndex].fValid) {
391 prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize;
392 continue;
393 }
394
395 SkScalar s, t;
396 SkPoint intersection;
397 if (compute_intersection(edgeData[prevIndex].fInset, edgeData[currIndex].fInset,
398 &intersection, &s, &t)) {
399 // if new intersection is further back on previous inset from the prior intersection
400 if (s < edgeData[prevIndex].fTValue) {
401 // no point in considering this one again
402 edgeData[prevIndex].fValid = false;
403 --insetVertexCount;
404 // go back one segment
405 prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize;
406 // we've already considered this intersection, we're done
407 } else if (edgeData[currIndex].fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500408 SkPointPriv::EqualsWithinTolerance(intersection,
409 edgeData[currIndex].fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000410 1.0e-6f)) {
411 break;
412 } else {
413 // add intersection
414 edgeData[currIndex].fIntersection = intersection;
415 edgeData[currIndex].fTValue = t;
416
417 // go to next segment
418 prevIndex = currIndex;
419 currIndex = (currIndex + 1) % inputPolygonSize;
420 }
421 } else {
422 // if prev to right side of curr
423 int side = winding*compute_side(edgeData[currIndex].fInset.fP0,
424 edgeData[currIndex].fInset.fP1,
425 edgeData[prevIndex].fInset.fP1);
426 if (side < 0 && side == winding*compute_side(edgeData[currIndex].fInset.fP0,
427 edgeData[currIndex].fInset.fP1,
428 edgeData[prevIndex].fInset.fP0)) {
429 // no point in considering this one again
430 edgeData[prevIndex].fValid = false;
431 --insetVertexCount;
432 // go back one segment
433 prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize;
434 } else {
435 // move to next segment
436 edgeData[currIndex].fValid = false;
437 --insetVertexCount;
438 currIndex = (currIndex + 1) % inputPolygonSize;
439 }
440 }
441 }
442
Jim Van Verthda965502017-04-11 15:29:14 -0400443 // store all the valid intersections that aren't nearly coincident
444 // TODO: look at the main algorithm and see if we can detect these better
445 static constexpr SkScalar kCleanupTolerance = 0.01f;
446
Brian Salomonab664fa2017-03-24 16:07:20 +0000447 insetPolygon->reset();
Mike Klein23e45442018-04-19 09:29:02 -0400448 if (insetVertexCount >= 0) {
449 insetPolygon->setReserve(insetVertexCount);
450 }
Jim Van Verthda965502017-04-11 15:29:14 -0400451 currIndex = -1;
Brian Salomonab664fa2017-03-24 16:07:20 +0000452 for (int i = 0; i < inputPolygonSize; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400453 if (edgeData[i].fValid && (currIndex == -1 ||
Cary Clarkdf429f32017-11-08 11:44:31 -0500454 !SkPointPriv::EqualsWithinTolerance(edgeData[i].fIntersection,
455 (*insetPolygon)[currIndex],
456 kCleanupTolerance))) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000457 *insetPolygon->push() = edgeData[i].fIntersection;
Jim Van Verthda965502017-04-11 15:29:14 -0400458 currIndex++;
Brian Salomonab664fa2017-03-24 16:07:20 +0000459 }
460 }
Jim Van Verthda965502017-04-11 15:29:14 -0400461 // make sure the first and last points aren't coincident
462 if (currIndex >= 1 &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500463 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
464 kCleanupTolerance)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400465 insetPolygon->pop();
466 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000467
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400468 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000469}
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400470
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400471///////////////////////////////////////////////////////////////////////////////////////////
472
473// compute the number of points needed for a circular join when offsetting a reflex vertex
Jim Van Verth061cc212018-07-11 14:09:09 -0400474bool SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar r,
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400475 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400476 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
477
478 SkScalar rCos = v1.dot(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400479 if (!SkScalarIsFinite(rCos)) {
480 return false;
481 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400482 SkScalar rSin = v1.cross(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400483 if (!SkScalarIsFinite(rSin)) {
484 return false;
485 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400486 SkScalar theta = SkScalarATan2(rSin, rCos);
487
488 int steps = SkScalarRoundToInt(SkScalarAbs(r*theta*kRecipPixelsPerArcSegment));
489
Jim Van Verth061cc212018-07-11 14:09:09 -0400490 SkScalar dTheta = steps > 0 ? theta / steps : 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400491 *rotSin = SkScalarSinCos(dTheta, rotCos);
492 *n = steps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400493 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400494}
495
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400496///////////////////////////////////////////////////////////////////////////////////////////
497
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400498// a point is "left" to another if its x coordinate is less, or if equal, its y coordinate
499static bool left(const SkPoint& p0, const SkPoint& p1) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400500 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY < p1.fY);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400501}
502
503struct Vertex {
504 static bool Left(const Vertex& qv0, const Vertex& qv1) {
505 return left(qv0.fPosition, qv1.fPosition);
506 }
507 // packed to fit into 16 bytes (one cache line)
508 SkPoint fPosition;
509 uint16_t fIndex; // index in unsorted polygon
510 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
511 uint16_t fNextIndex;
512 uint16_t fFlags;
513};
514
515enum VertexFlags {
516 kPrevLeft_VertexFlag = 0x1,
517 kNextLeft_VertexFlag = 0x2,
518};
519
520struct Edge {
521 // returns true if "this" is above "that"
522 bool above(const Edge& that, SkScalar tolerance = SK_ScalarNearlyZero) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400523 SkASSERT(this->fSegment.fP0.fX < that.fSegment.fP0.fX ||
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400524 SkScalarNearlyEqual(this->fSegment.fP0.fX, that.fSegment.fP0.fX, tolerance));
525 // The idea here is that if the vector between the origins of the two segments (dv)
526 // rotates counterclockwise up to the vector representing the "this" segment (u),
527 // then we know that "this" is above that. If the result is clockwise we say it's below.
528 SkVector dv = that.fSegment.fP0 - this->fSegment.fP0;
529 SkVector u = this->fSegment.fP1 - this->fSegment.fP0;
530 SkScalar cross = dv.cross(u);
531 if (cross > tolerance) {
532 return true;
533 } else if (cross < -tolerance) {
534 return false;
535 }
536 // If the result is 0 then either the two origins are equal or the origin of "that"
537 // lies on dv. So then we try the same for the vector from the tail of "this"
538 // to the head of "that". Again, ccw means "this" is above "that".
539 dv = that.fSegment.fP1 - this->fSegment.fP0;
540 return (dv.cross(u) > tolerance);
541 }
542
543 bool intersect(const Edge& that) const {
544 SkPoint intersection;
545 SkScalar s, t;
546 // check first to see if these edges are neighbors in the polygon
547 if (this->fIndex0 == that.fIndex0 || this->fIndex1 == that.fIndex0 ||
548 this->fIndex0 == that.fIndex1 || this->fIndex1 == that.fIndex1) {
549 return false;
550 }
551 return compute_intersection(this->fSegment, that.fSegment, &intersection, &s, &t);
552 }
553
554 bool operator==(const Edge& that) const {
555 return (this->fIndex0 == that.fIndex0 && this->fIndex1 == that.fIndex1);
556 }
557
558 bool operator!=(const Edge& that) const {
559 return !operator==(that);
560 }
561
562 OffsetSegment fSegment;
563 int32_t fIndex0; // indices for previous and next vertex
564 int32_t fIndex1;
565};
566
567class EdgeList {
568public:
569 void reserve(int count) { fEdges.reserve(count); }
570
571 bool insert(const Edge& newEdge) {
572 // linear search for now (expected case is very few active edges)
573 int insertIndex = 0;
574 while (insertIndex < fEdges.count() && fEdges[insertIndex].above(newEdge)) {
575 ++insertIndex;
576 }
577 // if we intersect with the existing edge above or below us
578 // then we know this polygon is not simple, so don't insert, just fail
579 if (insertIndex > 0 && newEdge.intersect(fEdges[insertIndex - 1])) {
580 return false;
581 }
582 if (insertIndex < fEdges.count() && newEdge.intersect(fEdges[insertIndex])) {
583 return false;
584 }
585
586 fEdges.push_back();
587 for (int i = fEdges.count() - 1; i > insertIndex; --i) {
588 fEdges[i] = fEdges[i - 1];
589 }
590 fEdges[insertIndex] = newEdge;
591
592 return true;
593 }
594
595 bool remove(const Edge& edge) {
596 SkASSERT(fEdges.count() > 0);
597
598 // linear search for now (expected case is very few active edges)
599 int removeIndex = 0;
600 while (removeIndex < fEdges.count() && fEdges[removeIndex] != edge) {
601 ++removeIndex;
602 }
603 // we'd better find it or something is wrong
604 SkASSERT(removeIndex < fEdges.count());
605
606 // if we intersect with the edge above or below us
607 // then we know this polygon is not simple, so don't remove, just fail
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400608 if (removeIndex > 0 && fEdges[removeIndex].intersect(fEdges[removeIndex - 1])) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400609 return false;
610 }
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400611 if (removeIndex < fEdges.count() - 1) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400612 if (fEdges[removeIndex].intersect(fEdges[removeIndex + 1])) {
613 return false;
614 }
615 // copy over the old entry
616 memmove(&fEdges[removeIndex], &fEdges[removeIndex + 1],
617 sizeof(Edge)*(fEdges.count() - removeIndex - 1));
618 }
619
620 fEdges.pop_back();
621 return true;
622 }
623
624private:
625 SkSTArray<1, Edge> fEdges;
626};
627
628// Here we implement a sweep line algorithm to determine whether the provided points
629// represent a simple polygon, i.e., the polygon is non-self-intersecting.
630// We first insert the vertices into a priority queue sorting horizontally from left to right.
631// Then as we pop the vertices from the queue we generate events which indicate that an edge
632// should be added or removed from an edge list. If any intersections are detected in the edge
633// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400634bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400635 if (polygonSize < 3) {
636 return false;
637 }
638
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400639 SkTDPQueue <Vertex, Vertex::Left> vertexQueue;
640 EdgeList sweepLine;
641
642 sweepLine.reserve(polygonSize);
643 for (int i = 0; i < polygonSize; ++i) {
644 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -0400645 if (!polygon[i].isFinite()) {
646 return false;
647 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400648 newVertex.fPosition = polygon[i];
649 newVertex.fIndex = i;
650 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
651 newVertex.fNextIndex = (i + 1) % polygonSize;
652 newVertex.fFlags = 0;
653 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
654 newVertex.fFlags |= kPrevLeft_VertexFlag;
655 }
656 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
657 newVertex.fFlags |= kNextLeft_VertexFlag;
658 }
659 vertexQueue.insert(newVertex);
660 }
661
662 // pop each vertex from the queue and generate events depending on
663 // where it lies relative to its neighboring edges
664 while (vertexQueue.count() > 0) {
665 const Vertex& v = vertexQueue.peek();
666
667 // check edge to previous vertex
668 if (v.fFlags & kPrevLeft_VertexFlag) {
669 Edge edge{ { polygon[v.fPrevIndex], v.fPosition }, v.fPrevIndex, v.fIndex };
670 if (!sweepLine.remove(edge)) {
671 break;
672 }
673 } else {
674 Edge edge{ { v.fPosition, polygon[v.fPrevIndex] }, v.fIndex, v.fPrevIndex };
675 if (!sweepLine.insert(edge)) {
676 break;
677 }
678 }
679
680 // check edge to next vertex
681 if (v.fFlags & kNextLeft_VertexFlag) {
682 Edge edge{ { polygon[v.fNextIndex], v.fPosition }, v.fNextIndex, v.fIndex };
683 if (!sweepLine.remove(edge)) {
684 break;
685 }
686 } else {
687 Edge edge{ { v.fPosition, polygon[v.fNextIndex] }, v.fIndex, v.fNextIndex };
688 if (!sweepLine.insert(edge)) {
689 break;
690 }
691 }
692
693 vertexQueue.pop();
694 }
695
696 return (vertexQueue.count() == 0);
697}
698
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400699///////////////////////////////////////////////////////////////////////////////////////////
700
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400701bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400702 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
703 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400704 if (inputPolygonSize < 3) {
705 return false;
706 }
707
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400708 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400709 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400710 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400711 return false;
712 }
713
Jim Van Verthbdde4282018-06-14 09:09:18 -0400714 // build normals
715 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
716 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
717 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400718 if (!SkScalarIsFinite(currOffset)) {
719 return false;
720 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400721 for (int curr = 0; curr < inputPolygonSize; ++curr) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400722 if (!inputPolygonVerts[curr].isFinite()) {
723 return false;
724 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400725 int next = (curr + 1) % inputPolygonSize;
726 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[next]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400727 if (!SkScalarIsFinite(nextOffset)) {
728 return false;
729 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400730 if (!compute_offset_vectors(inputPolygonVerts[curr], inputPolygonVerts[next],
731 currOffset, nextOffset, winding,
732 &normal0[curr], &normal1[next])) {
733 return false;
734 }
735 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400736 }
737
738 // build initial offset edge list
739 SkSTArray<64, EdgeData> edgeData(inputPolygonSize);
740 int prevIndex = inputPolygonSize - 1;
741 int currIndex = 0;
742 int nextIndex = 1;
743 while (currIndex < inputPolygonSize) {
744 int side = compute_side(inputPolygonVerts[prevIndex],
745 inputPolygonVerts[currIndex],
746 inputPolygonVerts[nextIndex]);
Jim Van Verthbdde4282018-06-14 09:09:18 -0400747 SkScalar offset = offsetDistanceFunc(inputPolygonVerts[currIndex]);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400748 // if reflex point, fill in curve
749 if (side*winding*offset < 0) {
750 SkScalar rotSin, rotCos;
751 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400752 SkVector prevNormal = normal1[currIndex];
Jim Van Verth061cc212018-07-11 14:09:09 -0400753 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
754 &rotSin, &rotCos, &numSteps)) {
755 return false;
756 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400757 for (int i = 0; i < numSteps - 1; ++i) {
758 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
759 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
760 EdgeData& edge = edgeData.push_back();
761 edge.fInset.fP0 = inputPolygonVerts[currIndex] + prevNormal;
762 edge.fInset.fP1 = inputPolygonVerts[currIndex] + currNormal;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400763 edge.init(currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400764 prevNormal = currNormal;
765 }
766 EdgeData& edge = edgeData.push_back();
767 edge.fInset.fP0 = inputPolygonVerts[currIndex] + prevNormal;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400768 edge.fInset.fP1 = inputPolygonVerts[currIndex] + normal0[currIndex];
Jim Van Verth872da6b2018-04-10 11:24:11 -0400769 edge.init(currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400770 }
771
772 // Add the edge
773 EdgeData& edge = edgeData.push_back();
Jim Van Verthbdde4282018-06-14 09:09:18 -0400774 edge.fInset.fP0 = inputPolygonVerts[currIndex] + normal0[currIndex];
775 edge.fInset.fP1 = inputPolygonVerts[nextIndex] + normal1[nextIndex];
Jim Van Verth872da6b2018-04-10 11:24:11 -0400776 edge.init(currIndex, nextIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400777
778 prevIndex = currIndex;
779 currIndex++;
780 nextIndex = (nextIndex + 1) % inputPolygonSize;
781 }
782
783 int edgeDataSize = edgeData.count();
784 prevIndex = edgeDataSize - 1;
785 currIndex = 0;
786 int insetVertexCount = edgeDataSize;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400787 int iterations = 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400788 while (prevIndex != currIndex) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400789 ++iterations;
790 // we should check each edge against each other edge at most once
791 if (iterations > edgeDataSize*edgeDataSize) {
792 return false;
793 }
794
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400795 if (!edgeData[prevIndex].fValid) {
796 prevIndex = (prevIndex + edgeDataSize - 1) % edgeDataSize;
797 continue;
798 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400799 if (!edgeData[currIndex].fValid) {
800 currIndex = (currIndex + 1) % edgeDataSize;
801 continue;
802 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400803
804 SkScalar s, t;
805 SkPoint intersection;
806 if (compute_intersection(edgeData[prevIndex].fInset, edgeData[currIndex].fInset,
807 &intersection, &s, &t)) {
808 // if new intersection is further back on previous inset from the prior intersection
809 if (s < edgeData[prevIndex].fTValue) {
810 // no point in considering this one again
811 edgeData[prevIndex].fValid = false;
812 --insetVertexCount;
813 // go back one segment
814 prevIndex = (prevIndex + edgeDataSize - 1) % edgeDataSize;
815 // we've already considered this intersection, we're done
816 } else if (edgeData[currIndex].fTValue > SK_ScalarMin &&
817 SkPointPriv::EqualsWithinTolerance(intersection,
818 edgeData[currIndex].fIntersection,
819 1.0e-6f)) {
820 break;
821 } else {
822 // add intersection
823 edgeData[currIndex].fIntersection = intersection;
824 edgeData[currIndex].fTValue = t;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400825 edgeData[currIndex].fIndex = edgeData[prevIndex].fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400826
827 // go to next segment
828 prevIndex = currIndex;
829 currIndex = (currIndex + 1) % edgeDataSize;
830 }
831 } else {
832 // If there is no intersection, we want to minimize the distance between
833 // the point where the segment lines cross and the segments themselves.
834 SkScalar prevPrevIndex = (prevIndex + edgeDataSize - 1) % edgeDataSize;
835 SkScalar currNextIndex = (currIndex + 1) % edgeDataSize;
836 SkScalar dist0 = compute_crossing_distance(edgeData[currIndex].fInset,
837 edgeData[prevPrevIndex].fInset);
838 SkScalar dist1 = compute_crossing_distance(edgeData[prevIndex].fInset,
839 edgeData[currNextIndex].fInset);
840 if (dist0 < dist1) {
841 edgeData[prevIndex].fValid = false;
842 prevIndex = prevPrevIndex;
843 } else {
844 edgeData[currIndex].fValid = false;
845 currIndex = currNextIndex;
846 }
847 --insetVertexCount;
848 }
849 }
850
851 // store all the valid intersections that aren't nearly coincident
852 // TODO: look at the main algorithm and see if we can detect these better
853 static constexpr SkScalar kCleanupTolerance = 0.01f;
854
855 offsetPolygon->reset();
856 offsetPolygon->setReserve(insetVertexCount);
857 currIndex = -1;
858 for (int i = 0; i < edgeData.count(); ++i) {
859 if (edgeData[i].fValid && (currIndex == -1 ||
860 !SkPointPriv::EqualsWithinTolerance(edgeData[i].fIntersection,
861 (*offsetPolygon)[currIndex],
862 kCleanupTolerance))) {
863 *offsetPolygon->push() = edgeData[i].fIntersection;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400864 if (polygonIndices) {
865 *polygonIndices->push() = edgeData[i].fIndex;
866 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400867 currIndex++;
868 }
869 }
870 // make sure the first and last points aren't coincident
871 if (currIndex >= 1 &&
872 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
873 kCleanupTolerance)) {
874 offsetPolygon->pop();
Jim Van Verth872da6b2018-04-10 11:24:11 -0400875 if (polygonIndices) {
876 polygonIndices->pop();
877 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400878 }
879
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400880 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400881 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400882
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400883 return (winding*offsetWinding > 0 &&
884 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400885}
886
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400887//////////////////////////////////////////////////////////////////////////////////////////
888
889struct TriangulationVertex {
890 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
891
892 enum class VertexType { kConvex, kReflex };
893
894 SkPoint fPosition;
895 VertexType fVertexType;
896 uint16_t fIndex;
897 uint16_t fPrevIndex;
898 uint16_t fNextIndex;
899};
900
901// test to see if point p is in triangle p0p1p2.
902// for now assuming strictly inside -- if on the edge it's outside
903static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
904 const SkPoint& p) {
905 SkVector v0 = p1 - p0;
906 SkVector v1 = p2 - p1;
907 SkScalar n = v0.cross(v1);
908
909 SkVector w0 = p - p0;
910 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
911 return false;
912 }
913
914 SkVector w1 = p - p1;
915 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
916 return false;
917 }
918
919 SkVector v2 = p0 - p2;
920 SkVector w2 = p - p2;
921 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
922 return false;
923 }
924
925 return true;
926}
927
928// Data structure to track reflex vertices and check whether any are inside a given triangle
929class ReflexHash {
930public:
931 void add(TriangulationVertex* v) {
932 fReflexList.addToTail(v);
933 }
934
935 void remove(TriangulationVertex* v) {
936 fReflexList.remove(v);
937 }
938
Jim Van Verth061cc212018-07-11 14:09:09 -0400939 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
940 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400941 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
942 reflexIter != fReflexList.end(); ++reflexIter) {
943 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -0400944 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
945 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400946 return true;
947 }
948 }
949
950 return false;
951 }
952
953private:
954 // TODO: switch to an actual spatial hash
955 SkTInternalLList<TriangulationVertex> fReflexList;
956};
957
958// Check to see if a reflex vertex has become a convex vertex after clipping an ear
959static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
960 int winding, ReflexHash* reflexHash,
961 SkTInternalLList<TriangulationVertex>* convexList) {
962 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
963 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
964 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -0400965 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400966 p->fVertexType = TriangulationVertex::VertexType::kConvex;
967 reflexHash->remove(p);
968 p->fPrev = p->fNext = nullptr;
969 convexList->addToTail(p);
970 }
971 }
972}
973
974bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
975 SkTDArray<uint16_t>* triangleIndices) {
976 if (polygonSize < 3) {
977 return false;
978 }
979 // need to be able to represent all the vertices in the 16-bit indices
980 if (polygonSize >= (1 << 16)) {
981 return false;
982 }
983
984 // get winding direction
985 // TODO: we do this for all the polygon routines -- might be better to have the client
986 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400987 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400988 if (0 == winding) {
989 return false;
990 }
991
992 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
993 // TODO: possibly sort the convexList in some way to get better triangles
994 SkTInternalLList<TriangulationVertex> convexList;
995 ReflexHash reflexHash;
996 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
997 int prevIndex = polygonSize - 1;
998 int currIndex = 0;
999 int nextIndex = 1;
1000 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1001 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1002 for (int i = 0; i < polygonSize; ++i) {
1003 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1004 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1005 triangulationVertices[currIndex].fIndex = currIndex;
1006 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1007 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001008 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001009 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1010 convexList.addToTail(&triangulationVertices[currIndex]);
1011 } else {
1012 // We treat near collinear vertices as reflex
1013 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1014 reflexHash.add(&triangulationVertices[currIndex]);
1015 }
1016
1017 prevIndex = currIndex;
1018 currIndex = nextIndex;
1019 nextIndex = (currIndex + 1) % polygonSize;
1020 v0 = v1;
1021 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1022 }
1023
1024 // The general concept: We are trying to find three neighboring vertices where
1025 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1026 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1027 // we have triangulated the entire polygon.
1028 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1029 // noting that only convex vertices can be potential ears, and we only need to check whether
1030 // any reflex vertices lie inside the ear.
1031 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1032 int vertexCount = polygonSize;
1033 while (vertexCount > 3) {
1034 bool success = false;
1035 TriangulationVertex* earVertex = nullptr;
1036 TriangulationVertex* p0 = nullptr;
1037 TriangulationVertex* p2 = nullptr;
1038 // find a convex vertex to clip
1039 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1040 convexIter != convexList.end(); ++convexIter) {
1041 earVertex = *convexIter;
1042 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1043
1044 p0 = &triangulationVertices[earVertex->fPrevIndex];
1045 p2 = &triangulationVertices[earVertex->fNextIndex];
1046
1047 // see if any reflex vertices are inside the ear
1048 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001049 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001050 if (failed) {
1051 continue;
1052 }
1053
1054 // found one we can clip
1055 success = true;
1056 break;
1057 }
1058 // If we can't find any ears to clip, this probably isn't a simple polygon
1059 if (!success) {
1060 return false;
1061 }
1062
1063 // add indices
1064 auto indices = triangleIndices->append(3);
1065 indices[0] = indexMap[p0->fIndex];
1066 indices[1] = indexMap[earVertex->fIndex];
1067 indices[2] = indexMap[p2->fIndex];
1068
1069 // clip the ear
1070 convexList.remove(earVertex);
1071 --vertexCount;
1072
1073 // reclassify reflex verts
1074 p0->fNextIndex = earVertex->fNextIndex;
1075 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1076
1077 p2->fPrevIndex = earVertex->fPrevIndex;
1078 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1079 }
1080
1081 // output indices
1082 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1083 vertexIter != convexList.end(); ++vertexIter) {
1084 TriangulationVertex* vertex = *vertexIter;
1085 *triangleIndices->push() = indexMap[vertex->fIndex];
1086 }
1087
1088 return true;
1089}