blob: a23942c46927bcead6817aa88ae1e74faf994c91 [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
Jim Van Verth00673692018-07-23 11:23:39 -040010#include <set>
Cary Clarkdf429f32017-11-08 11:44:31 -050011#include "SkPointPriv.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040012#include "SkTArray.h"
Brian Salomonab664fa2017-03-24 16:07:20 +000013#include "SkTemplates.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040014#include "SkTDPQueue.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040015#include "SkTInternalLList.h"
16
17//////////////////////////////////////////////////////////////////////////////////
18// Helper data structures and functions
Brian Salomonab664fa2017-03-24 16:07:20 +000019
Jim Van Verth4db18ed2018-04-03 10:00:37 -040020struct OffsetSegment {
Brian Salomonab664fa2017-03-24 16:07:20 +000021 SkPoint fP0;
Jim Van Vertha6316832018-07-24 09:30:37 -040022 SkVector fV;
Brian Salomonab664fa2017-03-24 16:07:20 +000023};
24
Jim Van Verthba4847c2018-08-07 16:02:33 -040025constexpr SkScalar kCrossTolerance = SK_ScalarNearlyZero * SK_ScalarNearlyZero;
26
27// Computes perpDot for point p compared to segment defined by origin p0 and vector v.
Brian Salomonab664fa2017-03-24 16:07:20 +000028// A positive value means the point is to the left of the segment,
29// negative is to the right, 0 is collinear.
Jim Van Verthba4847c2018-08-07 16:02:33 -040030static int compute_side(const SkPoint& p0, const SkVector& v, const SkPoint& p) {
31 SkVector w = p - p0;
32 SkScalar perpDot = v.cross(w);
33 if (!SkScalarNearlyZero(perpDot, kCrossTolerance)) {
Brian Salomonab664fa2017-03-24 16:07:20 +000034 return ((perpDot > 0) ? 1 : -1);
35 }
36
37 return 0;
38}
39
Jim Van Verth6784ffa2018-07-03 16:12:39 -040040// Returns 1 for cw, -1 for ccw and 0 if zero signed area (either degenerate or self-intersecting)
41int SkGetPolygonWinding(const SkPoint* polygonVerts, int polygonSize) {
42 if (polygonSize < 3) {
43 return 0;
44 }
45
Jim Van Verth8664a1d2018-06-28 16:26:50 -040046 // compute area and use sign to determine winding
47 SkScalar quadArea = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040048 SkVector v0 = polygonVerts[1] - polygonVerts[0];
49 for (int curr = 1; curr < polygonSize - 1; ++curr) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040050 int next = (curr + 1) % polygonSize;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040051 SkVector v1 = polygonVerts[next] - polygonVerts[0];
52 quadArea += v0.cross(v1);
53 v0 = v1;
Brian Salomonab664fa2017-03-24 16:07:20 +000054 }
Jim Van Verthba4847c2018-08-07 16:02:33 -040055 if (SkScalarNearlyZero(quadArea, kCrossTolerance)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040056 return 0;
57 }
58 // 1 == ccw, -1 == cw
59 return (quadArea > 0) ? 1 : -1;
Brian Salomonab664fa2017-03-24 16:07:20 +000060}
61
Jim Van Verthbdde4282018-06-14 09:09:18 -040062// Helper function to compute the individual vector for non-equal offsets
63inline void compute_offset(SkScalar d, const SkPoint& polyPoint, int side,
64 const SkPoint& outerTangentIntersect, SkVector* v) {
65 SkScalar dsq = d * d;
66 SkVector dP = outerTangentIntersect - polyPoint;
67 SkScalar dPlenSq = SkPointPriv::LengthSqd(dP);
Jim Van Verthba4847c2018-08-07 16:02:33 -040068 if (SkScalarNearlyZero(dPlenSq, SK_ScalarNearlyZero*SK_ScalarNearlyZero)) {
Jim Van Verthbdde4282018-06-14 09:09:18 -040069 v->set(0, 0);
70 } else {
71 SkScalar discrim = SkScalarSqrt(dPlenSq - dsq);
72 v->fX = (dsq*dP.fX - side * d*dP.fY*discrim) / dPlenSq;
73 v->fY = (dsq*dP.fY + side * d*dP.fX*discrim) / dPlenSq;
74 }
75}
76
77// Compute difference vector to offset p0-p1 'd0' and 'd1' units in direction specified by 'side'
78bool compute_offset_vectors(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
79 int side, SkPoint* vector0, SkPoint* vector1) {
Jim Van Verthda965502017-04-11 15:29:14 -040080 SkASSERT(side == -1 || side == 1);
Jim Van Verthda965502017-04-11 15:29:14 -040081 if (SkScalarNearlyEqual(d0, d1)) {
82 // if distances are equal, can just outset by the perpendicular
Jim Van Verthbdde4282018-06-14 09:09:18 -040083 SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX);
Jim Van Verthda965502017-04-11 15:29:14 -040084 perp.setLength(d0*side);
Jim Van Verthbdde4282018-06-14 09:09:18 -040085 *vector0 = perp;
86 *vector1 = perp;
Jim Van Verthda965502017-04-11 15:29:14 -040087 } else {
Jim Van Verthbdde4282018-06-14 09:09:18 -040088 SkScalar d0abs = SkTAbs(d0);
89 SkScalar d1abs = SkTAbs(d1);
Jim Van Verthda965502017-04-11 15:29:14 -040090 // Otherwise we need to compute the outer tangent.
91 // See: http://www.ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm
Jim Van Verthbdde4282018-06-14 09:09:18 -040092 if (d0abs < d1abs) {
Jim Van Verthda965502017-04-11 15:29:14 -040093 side = -side;
94 }
Jim Van Verthbdde4282018-06-14 09:09:18 -040095 SkScalar dD = d0abs - d1abs;
Jim Van Verthda965502017-04-11 15:29:14 -040096 // if one circle is inside another, we can't compute an offset
Cary Clarkdf429f32017-11-08 11:44:31 -050097 if (dD*dD >= SkPointPriv::DistanceToSqd(p0, p1)) {
Jim Van Verthda965502017-04-11 15:29:14 -040098 return false;
99 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400100 SkPoint outerTangentIntersect = SkPoint::Make((p1.fX*d0abs - p0.fX*d1abs) / dD,
101 (p1.fY*d0abs - p0.fY*d1abs) / dD);
Jim Van Verthda965502017-04-11 15:29:14 -0400102
Jim Van Verthbdde4282018-06-14 09:09:18 -0400103 compute_offset(d0, p0, side, outerTangentIntersect, vector0);
104 compute_offset(d1, p1, side, outerTangentIntersect, vector1);
Jim Van Verthda965502017-04-11 15:29:14 -0400105 }
106
107 return true;
Brian Salomonab664fa2017-03-24 16:07:20 +0000108}
109
Jim Van Verthbdde4282018-06-14 09:09:18 -0400110// Offset line segment p0-p1 'd0' and 'd1' units in the direction specified by 'side'
111bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
112 int side, SkPoint* offset0, SkPoint* offset1) {
113 SkVector v0, v1;
114 if (!compute_offset_vectors(p0, p1, d0, d1, side, &v0, &v1)) {
115 return false;
116 }
117 *offset0 = p0 + v0;
118 *offset1 = p1 + v1;
119
120 return true;
121}
122
Jim Van Verthba4847c2018-08-07 16:02:33 -0400123// check interval to see if intersection is in segment
124static inline bool outside_interval(SkScalar numer, SkScalar denom, bool denomPositive) {
125 return (denomPositive && (numer < 0 || numer > denom)) ||
126 (!denomPositive && (numer > 0 || numer < denom));
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400127}
128
Brian Salomonab664fa2017-03-24 16:07:20 +0000129// Compute the intersection 'p' between segments s0 and s1, if any.
130// 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'.
131// Returns false if there is no intersection.
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400132static bool compute_intersection(const OffsetSegment& s0, const OffsetSegment& s1,
Brian Salomonab664fa2017-03-24 16:07:20 +0000133 SkPoint* p, SkScalar* s, SkScalar* t) {
Jim Van Vertha6316832018-07-24 09:30:37 -0400134 const SkVector& v0 = s0.fV;
135 const SkVector& v1 = s1.fV;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400136 SkVector w = s1.fP0 - s0.fP0;
137 SkScalar denom = v0.cross(v1);
138 bool denomPositive = (denom > 0);
139 SkScalar sNumer, tNumer;
140 if (SkScalarNearlyZero(denom, kCrossTolerance)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400141 // segments are parallel, but not collinear
Jim Van Verthba4847c2018-08-07 16:02:33 -0400142 if (!SkScalarNearlyZero(w.cross(v0), kCrossTolerance) ||
143 !SkScalarNearlyZero(w.cross(v1), kCrossTolerance)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400144 return false;
145 }
146
Jim Van Verthba4847c2018-08-07 16:02:33 -0400147 // Check for zero-length segments
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400148 if (!SkPointPriv::CanNormalize(v0.fX, v0.fY)) {
Jim Van Verthba4847c2018-08-07 16:02:33 -0400149 // Both are zero-length
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400150 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
151 // Check if they're the same point
Jim Van Verthba4847c2018-08-07 16:02:33 -0400152 if (!SkPointPriv::CanNormalize(w.fX, w.fY)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400153 *p = s0.fP0;
154 *s = 0;
155 *t = 0;
156 return true;
157 } else {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400158 return false;
159 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400160 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400161 // Otherwise project segment0's origin onto segment1
162 tNumer = v1.dot(-w);
163 denom = v1.dot(v1);
164 if (outside_interval(tNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400165 return false;
166 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400167 sNumer = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400168 } else {
169 // Project segment1's endpoints onto segment0
Jim Van Verthba4847c2018-08-07 16:02:33 -0400170 sNumer = v0.dot(w);
171 denom = v0.dot(v0);
172 tNumer = 0;
173 if (outside_interval(sNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400174 // The first endpoint doesn't lie on segment0
175 // If segment1 is degenerate, then there's no collision
176 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
177 return false;
178 }
179
180 // Otherwise try the other one
Jim Van Verthba4847c2018-08-07 16:02:33 -0400181 SkScalar oldSNumer = sNumer;
182 sNumer = v0.dot(w + v1);
183 tNumer = denom;
184 if (outside_interval(sNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400185 // it's possible that segment1's interval surrounds segment0
186 // this is false if params have the same signs, and in that case no collision
Jim Van Verthba4847c2018-08-07 16:02:33 -0400187 if (sNumer*oldSNumer > 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400188 return false;
189 }
190 // otherwise project segment0's endpoint onto segment1 instead
Jim Van Verthba4847c2018-08-07 16:02:33 -0400191 sNumer = 0;
192 tNumer = v1.dot(-w);
193 denom = v1.dot(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400194 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400195 }
196 }
197 } else {
Jim Van Verthba4847c2018-08-07 16:02:33 -0400198 sNumer = w.cross(v1);
199 if (outside_interval(sNumer, denom, denomPositive)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400200 return false;
201 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400202 tNumer = w.cross(v0);
203 if (outside_interval(tNumer, denom, denomPositive)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400204 return false;
205 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000206 }
207
Jim Van Verthba4847c2018-08-07 16:02:33 -0400208 SkScalar localS = sNumer/denom;
209 SkScalar localT = tNumer/denom;
210
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400211 *p = s0.fP0 + v0*localS;
Brian Salomonab664fa2017-03-24 16:07:20 +0000212 *s = localS;
213 *t = localT;
214
215 return true;
216}
217
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400218bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
219 if (polygonSize < 3) {
220 return false;
Jim Van Verth0513f142017-03-24 14:28:57 -0400221 }
222
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400223 SkScalar lastArea = 0;
224 SkScalar lastPerpDot = 0;
Jim Van Verth0513f142017-03-24 14:28:57 -0400225
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400226 int prevIndex = polygonSize - 1;
227 int currIndex = 0;
228 int nextIndex = 1;
229 SkPoint origin = polygonVerts[0];
230 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
231 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
232 SkVector w0 = polygonVerts[currIndex] - origin;
233 SkVector w1 = polygonVerts[nextIndex] - origin;
234 for (int i = 0; i < polygonSize; ++i) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400235 if (!polygonVerts[i].isFinite()) {
236 return false;
237 }
238
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400239 // Check that winding direction is always the same (otherwise we have a reflex vertex)
Jim Van Verth0513f142017-03-24 14:28:57 -0400240 SkScalar perpDot = v0.cross(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400241 if (lastPerpDot*perpDot < 0) {
Jim Van Verth0513f142017-03-24 14:28:57 -0400242 return false;
243 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400244 if (0 != perpDot) {
245 lastPerpDot = perpDot;
246 }
247
248 // If the signed area ever flips it's concave
249 // TODO: see if we can verify convexity only with signed area
250 SkScalar quadArea = w0.cross(w1);
251 if (quadArea*lastArea < 0) {
252 return false;
253 }
254 if (0 != quadArea) {
255 lastArea = quadArea;
256 }
257
258 prevIndex = currIndex;
259 currIndex = nextIndex;
260 nextIndex = (currIndex + 1) % polygonSize;
261 v0 = v1;
262 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
263 w0 = w1;
264 w1 = polygonVerts[nextIndex] - origin;
Jim Van Verth0513f142017-03-24 14:28:57 -0400265 }
266
267 return true;
268}
Jim Van Verth0513f142017-03-24 14:28:57 -0400269
Jim Van Verth00673692018-07-23 11:23:39 -0400270struct OffsetEdge {
271 OffsetEdge* fPrev;
272 OffsetEdge* fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400273 OffsetSegment fInset;
274 SkPoint fIntersection;
275 SkScalar fTValue;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400276 uint16_t fIndex;
Jim Van Vertha6316832018-07-24 09:30:37 -0400277 uint16_t fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400278
Jim Van Verth00673692018-07-23 11:23:39 -0400279 void init(uint16_t start = 0, uint16_t end = 0) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400280 fIntersection = fInset.fP0;
281 fTValue = SK_ScalarMin;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400282 fIndex = start;
Jim Van Vertha6316832018-07-24 09:30:37 -0400283 fEnd = end;
284 }
285
286 // special intersection check that looks for endpoint intersection
287 bool checkIntersection(const OffsetEdge* that,
288 SkPoint* p, SkScalar* s, SkScalar* t) {
289 if (this->fEnd == that->fIndex) {
290 SkPoint p1 = this->fInset.fP0 + this->fInset.fV;
291 if (SkPointPriv::EqualsWithinTolerance(p1, that->fInset.fP0)) {
292 *p = p1;
293 *s = SK_Scalar1;
294 *t = 0;
295 return true;
296 }
297 }
298
299 return compute_intersection(this->fInset, that->fInset, p, s, t);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400300 }
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400301
Jim Van Verthba4847c2018-08-07 16:02:33 -0400302 // computes the line intersection and then the "distance" from that to this
303 // this is really a signed squared distance, where negative means that
304 // the intersection lies inside this->fInset
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400305 SkScalar computeCrossingDistance(const OffsetEdge* that) {
306 const OffsetSegment& s0 = this->fInset;
307 const OffsetSegment& s1 = that->fInset;
308 const SkVector& v0 = s0.fV;
309 const SkVector& v1 = s1.fV;
310
Jim Van Verthba4847c2018-08-07 16:02:33 -0400311 SkScalar denom = v0.cross(v1);
312 if (SkScalarNearlyZero(denom, kCrossTolerance)) {
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400313 // segments are parallel
314 return SK_ScalarMax;
315 }
316
Jim Van Verthba4847c2018-08-07 16:02:33 -0400317 SkVector w = s1.fP0 - s0.fP0;
318 SkScalar localS = w.cross(v1) / denom;
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400319 if (localS < 0) {
320 localS = -localS;
321 } else {
322 localS -= SK_Scalar1;
323 }
324
Jim Van Verthba4847c2018-08-07 16:02:33 -0400325 localS *= SkScalarAbs(localS);
326 localS *= v0.dot(v0);
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400327
328 return localS;
329 }
330
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400331};
332
Jim Van Verth00673692018-07-23 11:23:39 -0400333static void remove_node(const OffsetEdge* node, OffsetEdge** head) {
334 // remove from linked list
335 node->fPrev->fNext = node->fNext;
336 node->fNext->fPrev = node->fPrev;
337 if (node == *head) {
338 *head = (node->fNext == node) ? nullptr : node->fNext;
339 }
340}
341
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400342//////////////////////////////////////////////////////////////////////////////////
343
Brian Salomonab664fa2017-03-24 16:07:20 +0000344// The objective here is to inset all of the edges by the given distance, and then
345// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
346// we should only be making left-hand turns (for cw polygons, we use the winding
347// parameter to reverse this). We detect this by checking whether the second intersection
348// on an edge is closer to its tail than the first one.
349//
350// We might also have the case that there is no intersection between two neighboring inset edges.
351// In this case, one edge will lie to the right of the other and should be discarded along with
352// its previous intersection (if any).
353//
354// Note: the assumption is that inputPolygon is convex and has no coincident points.
355//
356bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400357 std::function<SkScalar(const SkPoint&)> insetDistanceFunc,
Jim Van Verthda965502017-04-11 15:29:14 -0400358 SkTDArray<SkPoint>* insetPolygon) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000359 if (inputPolygonSize < 3) {
360 return false;
361 }
362
Jim Van Vertha6316832018-07-24 09:30:37 -0400363 // restrict this to match other routines
364 // practically we don't want anything bigger than this anyway
365 if (inputPolygonSize >= (1 << 16)) {
366 return false;
367 }
368
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400369 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400370 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Brian Salomonab664fa2017-03-24 16:07:20 +0000371 if (0 == winding) {
372 return false;
373 }
374
375 // set up
Jim Van Verth00673692018-07-23 11:23:39 -0400376 SkAutoSTMalloc<64, OffsetEdge> edgeData(inputPolygonSize);
377 int prev = inputPolygonSize - 1;
378 for (int curr = 0; curr < inputPolygonSize; prev = curr, ++curr) {
379 int next = (curr + 1) % inputPolygonSize;
380 if (!inputPolygonVerts[curr].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400381 return false;
382 }
Jim Van Verthb55eb282017-07-18 14:13:45 -0400383 // check for convexity just to be sure
Jim Van Vertha6316832018-07-24 09:30:37 -0400384 if (compute_side(inputPolygonVerts[prev], inputPolygonVerts[curr] - inputPolygonVerts[prev],
Jim Van Verth00673692018-07-23 11:23:39 -0400385 inputPolygonVerts[next])*winding < 0) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400386 return false;
387 }
Jim Van Vertha6316832018-07-24 09:30:37 -0400388 SkPoint p0, p1;
Jim Van Verth00673692018-07-23 11:23:39 -0400389 if (!SkOffsetSegment(inputPolygonVerts[curr], inputPolygonVerts[next],
390 insetDistanceFunc(inputPolygonVerts[curr]),
391 insetDistanceFunc(inputPolygonVerts[next]),
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400392 winding,
Jim Van Vertha6316832018-07-24 09:30:37 -0400393 &p0, &p1)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400394 return false;
395 }
Jim Van Vertha6316832018-07-24 09:30:37 -0400396 edgeData[curr].fPrev = &edgeData[prev];
397 edgeData[curr].fNext = &edgeData[next];
398 edgeData[curr].fInset.fP0 = p0;
399 edgeData[curr].fInset.fV = p1 - p0;
Jim Van Verth00673692018-07-23 11:23:39 -0400400 edgeData[curr].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000401 }
402
Jim Van Verth00673692018-07-23 11:23:39 -0400403 OffsetEdge* head = &edgeData[0];
404 OffsetEdge* currEdge = head;
405 OffsetEdge* prevEdge = currEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000406 int insetVertexCount = inputPolygonSize;
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400407 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400408 while (head && prevEdge != currEdge) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400409 ++iterations;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400410 // we should check each edge against each other edge at most once
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400411 if (iterations > inputPolygonSize*inputPolygonSize) {
412 return false;
413 }
414
Brian Salomonab664fa2017-03-24 16:07:20 +0000415 SkScalar s, t;
416 SkPoint intersection;
Jim Van Verth00673692018-07-23 11:23:39 -0400417 if (compute_intersection(prevEdge->fInset, currEdge->fInset,
Brian Salomonab664fa2017-03-24 16:07:20 +0000418 &intersection, &s, &t)) {
419 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400420 if (s < prevEdge->fTValue) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000421 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400422 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000423 --insetVertexCount;
424 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400425 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000426 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400427 } else if (currEdge->fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500428 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400429 currEdge->fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000430 1.0e-6f)) {
431 break;
432 } else {
433 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400434 currEdge->fIntersection = intersection;
435 currEdge->fTValue = t;
Brian Salomonab664fa2017-03-24 16:07:20 +0000436
437 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400438 prevEdge = currEdge;
439 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000440 }
441 } else {
442 // if prev to right side of curr
Jim Van Verth00673692018-07-23 11:23:39 -0400443 int side = winding*compute_side(currEdge->fInset.fP0,
Jim Van Vertha6316832018-07-24 09:30:37 -0400444 currEdge->fInset.fV,
445 prevEdge->fInset.fP0);
446 if (side < 0 &&
447 side == winding*compute_side(currEdge->fInset.fP0,
448 currEdge->fInset.fV,
449 prevEdge->fInset.fP0 + prevEdge->fInset.fV)) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000450 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400451 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000452 --insetVertexCount;
453 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400454 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000455 } else {
456 // move to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400457 remove_node(currEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000458 --insetVertexCount;
Jim Van Verth00673692018-07-23 11:23:39 -0400459 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000460 }
461 }
462 }
463
Jim Van Verthda965502017-04-11 15:29:14 -0400464 // store all the valid intersections that aren't nearly coincident
465 // TODO: look at the main algorithm and see if we can detect these better
Brian Salomonab664fa2017-03-24 16:07:20 +0000466 insetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400467 if (head) {
468 static constexpr SkScalar kCleanupTolerance = 0.01f;
469 if (insetVertexCount >= 0) {
470 insetPolygon->setReserve(insetVertexCount);
Brian Salomonab664fa2017-03-24 16:07:20 +0000471 }
Jim Van Verth00673692018-07-23 11:23:39 -0400472 int currIndex = 0;
473 OffsetEdge* currEdge = head;
474 *insetPolygon->push() = currEdge->fIntersection;
475 currEdge = currEdge->fNext;
476 while (currEdge != head) {
477 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
478 (*insetPolygon)[currIndex],
479 kCleanupTolerance)) {
480 *insetPolygon->push() = currEdge->fIntersection;
481 currIndex++;
482 }
483 currEdge = currEdge->fNext;
484 }
485 // make sure the first and last points aren't coincident
486 if (currIndex >= 1 &&
487 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
488 kCleanupTolerance)) {
489 insetPolygon->pop();
490 }
Jim Van Verthda965502017-04-11 15:29:14 -0400491 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000492
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400493 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000494}
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400495
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400496///////////////////////////////////////////////////////////////////////////////////////////
497
498// compute the number of points needed for a circular join when offsetting a reflex vertex
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400499bool SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar offset,
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400500 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400501 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
502
503 SkScalar rCos = v1.dot(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400504 if (!SkScalarIsFinite(rCos)) {
505 return false;
506 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400507 SkScalar rSin = v1.cross(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400508 if (!SkScalarIsFinite(rSin)) {
509 return false;
510 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400511 SkScalar theta = SkScalarATan2(rSin, rCos);
512
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400513 SkScalar floatSteps = SkScalarAbs(offset*theta*kRecipPixelsPerArcSegment);
Jim Van Verth206dbe82018-07-23 11:48:31 -0400514 // limit the number of steps to at most max uint16_t (that's all we can index)
515 // knock one value off the top to account for rounding
516 if (floatSteps >= (1 << 16)-1) {
517 return false;
518 }
519 int steps = SkScalarRoundToInt(floatSteps);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400520
Jim Van Verth061cc212018-07-11 14:09:09 -0400521 SkScalar dTheta = steps > 0 ? theta / steps : 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400522 *rotSin = SkScalarSinCos(dTheta, rotCos);
523 *n = steps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400524 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400525}
526
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400527///////////////////////////////////////////////////////////////////////////////////////////
528
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400529// a point is "left" to another if its x coordinate is less, or if equal, its y coordinate
530static bool left(const SkPoint& p0, const SkPoint& p1) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400531 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY < p1.fY);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400532}
533
Jim Van Verthba4847c2018-08-07 16:02:33 -0400534// checks to see if a point that is collinear with a segment lies in the segment's range
535static bool in_segment(const SkPoint& p0, const SkVector& v, const SkPoint& p) {
536 SkVector w = p - p0;
537 SkScalar numer = w.dot(v);
538 return (numer >= 0 && numer <= v.dot(v));
539}
540
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400541struct Vertex {
542 static bool Left(const Vertex& qv0, const Vertex& qv1) {
543 return left(qv0.fPosition, qv1.fPosition);
544 }
Jim Van Verth00673692018-07-23 11:23:39 -0400545
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400546 // packed to fit into 16 bytes (one cache line)
547 SkPoint fPosition;
548 uint16_t fIndex; // index in unsorted polygon
549 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
550 uint16_t fNextIndex;
551 uint16_t fFlags;
552};
553
554enum VertexFlags {
555 kPrevLeft_VertexFlag = 0x1,
556 kNextLeft_VertexFlag = 0x2,
557};
558
Jim Van Verth00673692018-07-23 11:23:39 -0400559struct ActiveEdge {
Jim Van Vertha6316832018-07-24 09:30:37 -0400560 ActiveEdge(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1)
561 : fSegment({p0, p1-p0})
Jim Van Verth00673692018-07-23 11:23:39 -0400562 , fIndex0(index0)
563 , fIndex1(index1) {}
564
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400565 // returns true if "this" is above "that"
Jim Van Verth00673692018-07-23 11:23:39 -0400566 bool above(const ActiveEdge& that) const {
567 SkASSERT(this->fSegment.fP0.fX <= that.fSegment.fP0.fX);
Jim Van Vertha6316832018-07-24 09:30:37 -0400568 const SkVector& u = this->fSegment.fV;
569 SkVector dv = that.fSegment.fP0 - this->fSegment.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400570 // The idea here is that if the vector between the origins of the two segments (dv)
571 // rotates counterclockwise up to the vector representing the "this" segment (u),
572 // then we know that "this" is above that. If the result is clockwise we say it's below.
Jim Van Verth00673692018-07-23 11:23:39 -0400573 if (this->fIndex0 != that.fIndex0) {
Jim Van Verth00673692018-07-23 11:23:39 -0400574 SkScalar cross = dv.cross(u);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400575 if (cross > kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400576 return true;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400577 } else if (cross < -kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400578 return false;
579 }
580 } else if (this->fIndex1 == that.fIndex1) {
581 // they're the same edge
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400582 return false;
583 }
Jim Van Verth00673692018-07-23 11:23:39 -0400584 // At this point either the two origins are nearly equal or the origin of "that"
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400585 // lies on dv. So then we try the same for the vector from the tail of "this"
586 // to the head of "that". Again, ccw means "this" is above "that".
Jim Van Vertha6316832018-07-24 09:30:37 -0400587 // dv = that.P1 - this.P0
588 // = that.fP0 + that.fV - this.fP0
589 // = that.fP0 - this.fP0 + that.fV
590 // = old_dv + that.fV
591 dv += that.fSegment.fV;
Jim Van Verth00673692018-07-23 11:23:39 -0400592 SkScalar cross = dv.cross(u);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400593 if (cross > kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400594 return true;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400595 } else if (cross < -kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400596 return false;
597 }
598 // If the previous check fails, the two segments are nearly collinear
599 // First check y-coord of first endpoints
600 if (this->fSegment.fP0.fX < that.fSegment.fP0.fX) {
601 return (this->fSegment.fP0.fY >= that.fSegment.fP0.fY);
602 } else if (this->fSegment.fP0.fY > that.fSegment.fP0.fY) {
603 return true;
604 } else if (this->fSegment.fP0.fY < that.fSegment.fP0.fY) {
605 return false;
606 }
607 // The first endpoints are the same, so check the other endpoint
Jim Van Vertha6316832018-07-24 09:30:37 -0400608 SkPoint thisP1 = this->fSegment.fP0 + this->fSegment.fV;
609 SkPoint thatP1 = that.fSegment.fP0 + that.fSegment.fV;
610 if (thisP1.fX < thatP1.fX) {
611 return (thisP1.fY >= thatP1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400612 } else {
Jim Van Vertha6316832018-07-24 09:30:37 -0400613 return (thisP1.fY > thatP1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400614 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400615 }
616
Jim Van Verth00673692018-07-23 11:23:39 -0400617 bool intersect(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400618 // check first to see if these edges are neighbors in the polygon
619 if (this->fIndex0 == that.fIndex0 || this->fIndex1 == that.fIndex0 ||
620 this->fIndex0 == that.fIndex1 || this->fIndex1 == that.fIndex1) {
621 return false;
622 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400623
624 // We don't need the exact intersection point so we can do a simpler test here.
625 const SkPoint& p0 = this->fSegment.fP0;
626 const SkVector& v = this->fSegment.fV;
627 SkPoint p1 = p0 + v;
628 const SkPoint& q0 = that.fSegment.fP0;
629 const SkVector& w = that.fSegment.fV;
630 SkPoint q1 = q0 + w;
631
632 int side0 = compute_side(p0, v, q0);
633 int side1 = compute_side(p0, v, q1);
634 int side2 = compute_side(q0, w, p0);
635 int side3 = compute_side(q0, w, p1);
636
637 // if each segment straddles the other (i.e., the endpoints have different sides)
638 // then they intersect
639 if (side0*side1 < 0 && side2*side3 < 0) {
640 return true;
641 }
642
643 // check collinear cases
644 // q0 lies on segment0's line, see if it's inside the segment
645 if (side0 == 0 && in_segment(p0, v, q0)) {
646 return true;
647 }
648 // q0 + w lies on segment0's line, see if it's inside the segment
649 if (side1 == 0 && in_segment(p0, v, q1)) {
650 return true;
651 }
652 // p0 lies on segment1's line, see if it's inside the segment
653 if (side2 == 0 && in_segment(q0, w, p0)) {
654 return true;
655 }
656 // p0 + v lies on segment0's line, see if it's inside the segment
657 if (side3 == 0 && in_segment(q0, w, p1)) {
658 return true;
659 }
660
661 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400662 }
663
Jim Van Verth00673692018-07-23 11:23:39 -0400664 bool lessThan(const ActiveEdge& that) const {
665 if (this->fSegment.fP0.fX > that.fSegment.fP0.fX ||
666 (this->fSegment.fP0.fX == that.fSegment.fP0.fX &&
667 this->fSegment.fP0.fY < that.fSegment.fP0.fY)) {
668 return !that.above(*this);
669 }
670 return this->above(that);
671 }
672
673 bool operator<(const ActiveEdge& that) const {
674 SkASSERT(!this->lessThan(*this));
675 SkASSERT(!that.lessThan(that));
676 SkASSERT(!(this->lessThan(that) && that.lessThan(*this)));
677 return this->lessThan(that);
678 }
679
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400680 OffsetSegment fSegment;
Jim Van Vertha6316832018-07-24 09:30:37 -0400681 uint16_t fIndex0; // indices for previous and next vertex
682 uint16_t fIndex1;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400683};
684
Jim Van Verth00673692018-07-23 11:23:39 -0400685class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400686public:
Jim Van Vertha6316832018-07-24 09:30:37 -0400687 bool insert(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
Jim Van Verth00673692018-07-23 11:23:39 -0400688 std::pair<Iterator, bool> result = fEdgeTree.emplace(p0, p1, index0, index1);
689 if (!result.second) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400690 return false;
691 }
692
Jim Van Verth00673692018-07-23 11:23:39 -0400693 Iterator& curr = result.first;
694 if (curr != fEdgeTree.begin() && curr->intersect(*std::prev(curr))) {
695 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400696 }
Jim Van Verth00673692018-07-23 11:23:39 -0400697 Iterator next = std::next(curr);
698 if (next != fEdgeTree.end() && curr->intersect(*next)) {
699 return false;
700 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400701
702 return true;
703 }
704
Jim Van Verth00673692018-07-23 11:23:39 -0400705 bool remove(const ActiveEdge& edge) {
706 auto element = fEdgeTree.find(edge);
707 // this better not happen
708 if (element == fEdgeTree.end()) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400709 return false;
710 }
Jim Van Verth00673692018-07-23 11:23:39 -0400711 if (element != fEdgeTree.begin() && element->intersect(*std::prev(element))) {
712 return false;
713 }
714 Iterator next = std::next(element);
715 if (next != fEdgeTree.end() && element->intersect(*next)) {
716 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400717 }
718
Jim Van Verth00673692018-07-23 11:23:39 -0400719 fEdgeTree.erase(element);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400720 return true;
721 }
722
723private:
Jim Van Verth00673692018-07-23 11:23:39 -0400724 std::set<ActiveEdge> fEdgeTree;
725 typedef std::set<ActiveEdge>::iterator Iterator;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400726};
727
728// Here we implement a sweep line algorithm to determine whether the provided points
729// represent a simple polygon, i.e., the polygon is non-self-intersecting.
730// We first insert the vertices into a priority queue sorting horizontally from left to right.
731// Then as we pop the vertices from the queue we generate events which indicate that an edge
732// should be added or removed from an edge list. If any intersections are detected in the edge
733// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400734bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400735 if (polygonSize < 3) {
736 return false;
737 }
738
Jim Van Vertha6316832018-07-24 09:30:37 -0400739 // need to be able to represent all the vertices in the 16-bit indices
740 if (polygonSize >= (1 << 16)) {
741 return false;
742 }
743
Jim Van Verth00673692018-07-23 11:23:39 -0400744 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400745 for (int i = 0; i < polygonSize; ++i) {
746 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -0400747 if (!polygon[i].isFinite()) {
748 return false;
749 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400750 newVertex.fPosition = polygon[i];
751 newVertex.fIndex = i;
752 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
753 newVertex.fNextIndex = (i + 1) % polygonSize;
754 newVertex.fFlags = 0;
755 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
756 newVertex.fFlags |= kPrevLeft_VertexFlag;
757 }
758 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
759 newVertex.fFlags |= kNextLeft_VertexFlag;
760 }
761 vertexQueue.insert(newVertex);
762 }
763
764 // pop each vertex from the queue and generate events depending on
765 // where it lies relative to its neighboring edges
Jim Van Verth00673692018-07-23 11:23:39 -0400766 ActiveEdgeList sweepLine;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400767 while (vertexQueue.count() > 0) {
768 const Vertex& v = vertexQueue.peek();
769
770 // check edge to previous vertex
771 if (v.fFlags & kPrevLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400772 ActiveEdge edge(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400773 if (!sweepLine.remove(edge)) {
774 break;
775 }
776 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400777 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400778 break;
779 }
780 }
781
782 // check edge to next vertex
783 if (v.fFlags & kNextLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400784 ActiveEdge edge(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400785 if (!sweepLine.remove(edge)) {
786 break;
787 }
788 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400789 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400790 break;
791 }
792 }
793
794 vertexQueue.pop();
795 }
796
797 return (vertexQueue.count() == 0);
798}
799
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400800///////////////////////////////////////////////////////////////////////////////////////////
801
Jim Van Verth00673692018-07-23 11:23:39 -0400802// helper function for SkOffsetSimplePolygon
803static void setup_offset_edge(OffsetEdge* currEdge,
804 const SkPoint& endpoint0, const SkPoint& endpoint1,
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400805 uint16_t startIndex, uint16_t endIndex) {
Jim Van Verth00673692018-07-23 11:23:39 -0400806 currEdge->fInset.fP0 = endpoint0;
Jim Van Vertha6316832018-07-24 09:30:37 -0400807 currEdge->fInset.fV = endpoint1 - endpoint0;
Jim Van Verth00673692018-07-23 11:23:39 -0400808 currEdge->init(startIndex, endIndex);
809}
810
Jim Van Verth98d33752018-08-03 15:59:46 -0400811static bool is_reflex_vertex(const SkPoint* inputPolygonVerts, int winding, SkScalar offset,
812 uint16_t prevIndex, uint16_t currIndex, uint16_t nextIndex) {
813 int side = compute_side(inputPolygonVerts[prevIndex],
814 inputPolygonVerts[currIndex] - inputPolygonVerts[prevIndex],
815 inputPolygonVerts[nextIndex]);
816 // if reflex point, we need to add extra edges
817 return (side*winding*offset < 0);
818}
819
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400820bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400821 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
822 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400823 if (inputPolygonSize < 3) {
824 return false;
825 }
826
Jim Van Vertha6316832018-07-24 09:30:37 -0400827 // need to be able to represent all the vertices in the 16-bit indices
828 if (inputPolygonSize >= (1 << 16)) {
829 return false;
830 }
831
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400832 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400833 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400834 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400835 return false;
836 }
837
Jim Van Verthbdde4282018-06-14 09:09:18 -0400838 // build normals
839 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
840 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400841 SkAutoSTMalloc<64, SkScalar> offset(inputPolygonSize);
842 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400843 if (!SkScalarIsFinite(currOffset)) {
844 return false;
845 }
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400846 offset[0] = currOffset;
Jim Van Verth98d33752018-08-03 15:59:46 -0400847 int numEdges = 0;
848 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
849 currIndex < inputPolygonSize;
850 prevIndex = currIndex, ++currIndex) {
851 if (!inputPolygonVerts[currIndex].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400852 return false;
853 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400854 int nextIndex = (currIndex + 1) % inputPolygonSize;
855 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[nextIndex]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400856 if (!SkScalarIsFinite(nextOffset)) {
857 return false;
858 }
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400859 offset[nextIndex] = nextOffset;
Jim Van Verth98d33752018-08-03 15:59:46 -0400860 if (!compute_offset_vectors(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
Jim Van Verthbdde4282018-06-14 09:09:18 -0400861 currOffset, nextOffset, winding,
Jim Van Verth98d33752018-08-03 15:59:46 -0400862 &normal0[currIndex], &normal1[nextIndex])) {
Jim Van Verthbdde4282018-06-14 09:09:18 -0400863 return false;
864 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400865 if (currIndex > 0) {
866 // if reflex point, we need to add extra edges
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400867 if (is_reflex_vertex(inputPolygonVerts, winding, currOffset,
Jim Van Verth98d33752018-08-03 15:59:46 -0400868 prevIndex, currIndex, nextIndex)) {
869 SkScalar rotSin, rotCos;
870 int numSteps;
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400871 if (!SkComputeRadialSteps(normal1[currIndex], normal0[currIndex], currOffset,
Jim Van Verth98d33752018-08-03 15:59:46 -0400872 &rotSin, &rotCos, &numSteps)) {
873 return false;
874 }
875 numEdges += SkTMax(numSteps, 1);
876 }
877 }
878 numEdges++;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400879 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400880 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400881 // finish up the edge counting
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400882 if (is_reflex_vertex(inputPolygonVerts, winding, currOffset, inputPolygonSize-1, 0, 1)) {
Jim Van Verth98d33752018-08-03 15:59:46 -0400883 SkScalar rotSin, rotCos;
884 int numSteps;
885 if (!SkComputeRadialSteps(normal1[0], normal0[0], currOffset,
886 &rotSin, &rotCos, &numSteps)) {
887 return false;
888 }
889 numEdges += SkTMax(numSteps, 1);
890 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400891
892 // build initial offset edge list
Jim Van Verth98d33752018-08-03 15:59:46 -0400893 SkSTArray<64, OffsetEdge> edgeData(numEdges);
894 OffsetEdge* prevEdge = nullptr;
895 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
896 currIndex < inputPolygonSize;
897 prevIndex = currIndex, ++currIndex) {
898 int nextIndex = (currIndex + 1) % inputPolygonSize;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400899 // if reflex point, fill in curve
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400900 if (is_reflex_vertex(inputPolygonVerts, winding, offset[currIndex],
Jim Van Verth98d33752018-08-03 15:59:46 -0400901 prevIndex, currIndex, nextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400902 SkScalar rotSin, rotCos;
903 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400904 SkVector prevNormal = normal1[currIndex];
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400905 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], offset[currIndex],
Jim Van Verth061cc212018-07-11 14:09:09 -0400906 &rotSin, &rotCos, &numSteps)) {
907 return false;
908 }
Jim Van Verth00673692018-07-23 11:23:39 -0400909 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400910 for (int i = 0; i < numSteps - 1; ++i) {
911 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
912 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -0400913 setup_offset_edge(currEdge,
914 inputPolygonVerts[currIndex] + prevNormal,
915 inputPolygonVerts[currIndex] + currNormal,
916 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400917 prevNormal = currNormal;
Jim Van Verth98d33752018-08-03 15:59:46 -0400918 currEdge->fPrev = prevEdge;
919 if (prevEdge) {
920 prevEdge->fNext = currEdge;
921 }
922 prevEdge = currEdge;
Jim Van Verth00673692018-07-23 11:23:39 -0400923 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400924 }
Jim Van Verth00673692018-07-23 11:23:39 -0400925 setup_offset_edge(currEdge,
926 inputPolygonVerts[currIndex] + prevNormal,
927 inputPolygonVerts[currIndex] + normal0[currIndex],
928 currIndex, currIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -0400929 currEdge->fPrev = prevEdge;
930 if (prevEdge) {
931 prevEdge->fNext = currEdge;
932 }
933 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400934 }
935
936 // Add the edge
Jim Van Verth98d33752018-08-03 15:59:46 -0400937 auto currEdge = edgeData.push_back_n(1);
938 setup_offset_edge(currEdge,
Jim Van Verth00673692018-07-23 11:23:39 -0400939 inputPolygonVerts[currIndex] + normal0[currIndex],
940 inputPolygonVerts[nextIndex] + normal1[nextIndex],
941 currIndex, nextIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -0400942 currEdge->fPrev = prevEdge;
943 if (prevEdge) {
944 prevEdge->fNext = currEdge;
945 }
946 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400947 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400948 // close up the linked list
949 SkASSERT(prevEdge);
950 prevEdge->fNext = &edgeData[0];
951 edgeData[0].fPrev = prevEdge;
Jim Van Verth00673692018-07-23 11:23:39 -0400952
953 // now clip edges
Jim Van Verth98d33752018-08-03 15:59:46 -0400954 SkASSERT(edgeData.count() == numEdges);
Jim Van Verth00673692018-07-23 11:23:39 -0400955 auto head = &edgeData[0];
956 auto currEdge = head;
Jim Van Verth98d33752018-08-03 15:59:46 -0400957 int offsetVertexCount = numEdges;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400958 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400959 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400960 ++iterations;
961 // we should check each edge against each other edge at most once
Jim Van Verth98d33752018-08-03 15:59:46 -0400962 if (iterations > numEdges*numEdges) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400963 return false;
964 }
965
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400966 SkScalar s, t;
967 SkPoint intersection;
Jim Van Vertha6316832018-07-24 09:30:37 -0400968 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400969 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400970 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400971 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400972 remove_node(prevEdge, &head);
973 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400974 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400975 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400976 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400977 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400978 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400979 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400980 1.0e-6f)) {
981 break;
982 } else {
983 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400984 currEdge->fIntersection = intersection;
985 currEdge->fTValue = t;
986 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400987
988 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400989 prevEdge = currEdge;
990 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400991 }
992 } else {
993 // If there is no intersection, we want to minimize the distance between
994 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -0400995 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
996 OffsetEdge* currNextEdge = currEdge->fNext;
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400997 SkScalar dist0 = currEdge->computeCrossingDistance(prevPrevEdge);
998 SkScalar dist1 = prevEdge->computeCrossingDistance(currNextEdge);
999 // if both lead to direct collision
1000 if (dist0 < 0 && dist1 < 0) {
1001 // check first to see if either represent parts of one contour
1002 SkPoint p1 = prevPrevEdge->fInset.fP0 + prevPrevEdge->fInset.fV;
1003 bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
1004 prevEdge->fInset.fP0);
1005 p1 = currEdge->fInset.fP0 + currEdge->fInset.fV;
1006 bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
1007 currNextEdge->fInset.fP0);
1008
1009 // want to step along contour to find intersections rather than jump to new one
1010 if (currSameContour && !prevSameContour) {
1011 remove_node(currEdge, &head);
1012 currEdge = currNextEdge;
1013 --offsetVertexCount;
1014 continue;
1015 } else if (prevSameContour && !currSameContour) {
1016 remove_node(prevEdge, &head);
1017 prevEdge = prevPrevEdge;
1018 --offsetVertexCount;
1019 continue;
1020 }
1021 }
1022
1023 // otherwise minimize collision distance along segment
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001024 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -04001025 remove_node(prevEdge, &head);
1026 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001027 } else {
Jim Van Verth00673692018-07-23 11:23:39 -04001028 remove_node(currEdge, &head);
1029 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001030 }
Jim Van Verth00673692018-07-23 11:23:39 -04001031 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001032 }
1033 }
1034
1035 // store all the valid intersections that aren't nearly coincident
1036 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001037 offsetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -04001038 if (head) {
1039 static constexpr SkScalar kCleanupTolerance = 0.01f;
1040 if (offsetVertexCount >= 0) {
1041 offsetPolygon->setReserve(offsetVertexCount);
Ben Wagnere9dd3162018-07-18 21:27:57 +00001042 }
Jim Van Verth00673692018-07-23 11:23:39 -04001043 int currIndex = 0;
1044 OffsetEdge* currEdge = head;
1045 *offsetPolygon->push() = currEdge->fIntersection;
Ben Wagnere9dd3162018-07-18 21:27:57 +00001046 if (polygonIndices) {
Jim Van Verth00673692018-07-23 11:23:39 -04001047 *polygonIndices->push() = currEdge->fIndex;
1048 }
1049 currEdge = currEdge->fNext;
1050 while (currEdge != head) {
1051 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
1052 (*offsetPolygon)[currIndex],
1053 kCleanupTolerance)) {
1054 *offsetPolygon->push() = currEdge->fIntersection;
1055 if (polygonIndices) {
1056 *polygonIndices->push() = currEdge->fIndex;
1057 }
1058 currIndex++;
1059 }
1060 currEdge = currEdge->fNext;
1061 }
1062 // make sure the first and last points aren't coincident
1063 if (currIndex >= 1 &&
1064 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
1065 kCleanupTolerance)) {
1066 offsetPolygon->pop();
1067 if (polygonIndices) {
1068 polygonIndices->pop();
1069 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001070 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001071 }
1072
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001073 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001074 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001075
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001076 return (winding*offsetWinding > 0 &&
1077 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001078}
1079
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001080//////////////////////////////////////////////////////////////////////////////////////////
1081
1082struct TriangulationVertex {
1083 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
1084
1085 enum class VertexType { kConvex, kReflex };
1086
1087 SkPoint fPosition;
1088 VertexType fVertexType;
1089 uint16_t fIndex;
1090 uint16_t fPrevIndex;
1091 uint16_t fNextIndex;
1092};
1093
1094// test to see if point p is in triangle p0p1p2.
1095// for now assuming strictly inside -- if on the edge it's outside
1096static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1097 const SkPoint& p) {
1098 SkVector v0 = p1 - p0;
1099 SkVector v1 = p2 - p1;
1100 SkScalar n = v0.cross(v1);
1101
1102 SkVector w0 = p - p0;
1103 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
1104 return false;
1105 }
1106
1107 SkVector w1 = p - p1;
1108 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
1109 return false;
1110 }
1111
1112 SkVector v2 = p0 - p2;
1113 SkVector w2 = p - p2;
1114 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
1115 return false;
1116 }
1117
1118 return true;
1119}
1120
1121// Data structure to track reflex vertices and check whether any are inside a given triangle
1122class ReflexHash {
1123public:
1124 void add(TriangulationVertex* v) {
1125 fReflexList.addToTail(v);
1126 }
1127
1128 void remove(TriangulationVertex* v) {
1129 fReflexList.remove(v);
1130 }
1131
Jim Van Verth061cc212018-07-11 14:09:09 -04001132 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1133 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001134 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
1135 reflexIter != fReflexList.end(); ++reflexIter) {
1136 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -04001137 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
1138 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001139 return true;
1140 }
1141 }
1142
1143 return false;
1144 }
1145
1146private:
1147 // TODO: switch to an actual spatial hash
1148 SkTInternalLList<TriangulationVertex> fReflexList;
1149};
1150
1151// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1152static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1153 int winding, ReflexHash* reflexHash,
1154 SkTInternalLList<TriangulationVertex>* convexList) {
1155 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1156 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1157 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001158 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001159 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1160 reflexHash->remove(p);
1161 p->fPrev = p->fNext = nullptr;
1162 convexList->addToTail(p);
1163 }
1164 }
1165}
1166
1167bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1168 SkTDArray<uint16_t>* triangleIndices) {
1169 if (polygonSize < 3) {
1170 return false;
1171 }
1172 // need to be able to represent all the vertices in the 16-bit indices
1173 if (polygonSize >= (1 << 16)) {
1174 return false;
1175 }
1176
1177 // get winding direction
1178 // TODO: we do this for all the polygon routines -- might be better to have the client
1179 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001180 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001181 if (0 == winding) {
1182 return false;
1183 }
1184
1185 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1186 // TODO: possibly sort the convexList in some way to get better triangles
1187 SkTInternalLList<TriangulationVertex> convexList;
1188 ReflexHash reflexHash;
1189 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1190 int prevIndex = polygonSize - 1;
1191 int currIndex = 0;
1192 int nextIndex = 1;
1193 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1194 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1195 for (int i = 0; i < polygonSize; ++i) {
1196 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1197 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1198 triangulationVertices[currIndex].fIndex = currIndex;
1199 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1200 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001201 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001202 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1203 convexList.addToTail(&triangulationVertices[currIndex]);
1204 } else {
1205 // We treat near collinear vertices as reflex
1206 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1207 reflexHash.add(&triangulationVertices[currIndex]);
1208 }
1209
1210 prevIndex = currIndex;
1211 currIndex = nextIndex;
1212 nextIndex = (currIndex + 1) % polygonSize;
1213 v0 = v1;
1214 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1215 }
1216
1217 // The general concept: We are trying to find three neighboring vertices where
1218 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1219 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1220 // we have triangulated the entire polygon.
1221 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1222 // noting that only convex vertices can be potential ears, and we only need to check whether
1223 // any reflex vertices lie inside the ear.
1224 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1225 int vertexCount = polygonSize;
1226 while (vertexCount > 3) {
1227 bool success = false;
1228 TriangulationVertex* earVertex = nullptr;
1229 TriangulationVertex* p0 = nullptr;
1230 TriangulationVertex* p2 = nullptr;
1231 // find a convex vertex to clip
1232 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1233 convexIter != convexList.end(); ++convexIter) {
1234 earVertex = *convexIter;
1235 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1236
1237 p0 = &triangulationVertices[earVertex->fPrevIndex];
1238 p2 = &triangulationVertices[earVertex->fNextIndex];
1239
1240 // see if any reflex vertices are inside the ear
1241 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001242 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001243 if (failed) {
1244 continue;
1245 }
1246
1247 // found one we can clip
1248 success = true;
1249 break;
1250 }
1251 // If we can't find any ears to clip, this probably isn't a simple polygon
1252 if (!success) {
1253 return false;
1254 }
1255
1256 // add indices
1257 auto indices = triangleIndices->append(3);
1258 indices[0] = indexMap[p0->fIndex];
1259 indices[1] = indexMap[earVertex->fIndex];
1260 indices[2] = indexMap[p2->fIndex];
1261
1262 // clip the ear
1263 convexList.remove(earVertex);
1264 --vertexCount;
1265
1266 // reclassify reflex verts
1267 p0->fNextIndex = earVertex->fNextIndex;
1268 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1269
1270 p2->fPrevIndex = earVertex->fPrevIndex;
1271 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1272 }
1273
1274 // output indices
1275 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1276 vertexIter != convexList.end(); ++vertexIter) {
1277 TriangulationVertex* vertex = *vertexIter;
1278 *triangleIndices->push() = indexMap[vertex->fIndex];
1279 }
1280
1281 return true;
1282}